Is it possible to dynamically add code to/extend a class? - php

I want to write a sort of "plugin/module" system for my code, and it would make it much easier if I could "add" stuff into a class after it's been defined.
For example, something like this:
class foo {
public function a() {
return 'b';
}
}
There's the class. Now I want to add another function/variable/const to it, after it's defined.
I realize that this is probably not possible, but I need confirmation.

No, you cannot add methods to an already defined class at runtime.
But you can create similar functionality using __call/__callStatic magic methods.
Class Extendable {
private $handlers = array();
public function registerHandler($handler) {
$this->handlers[] = $handler;
}
public function __call($method, $arguments) {
foreach ($this->handlers as $handler) {
if (method_exists($handler, $method)) {
return call_user_func_array(
array($handler, $method),
$arguments
);
}
}
}
}
Class myclass extends Extendable {
public function foo() {
echo 'foo';
}
}
CLass myclass2 {
public function bar() {
echo 'bar';
}
}
$myclass = new myclass();
$myclass->registerHandler(new myclass2());
$myclass->foo(); // prints 'foo'
echo "\n";
$myclass->bar(); // prints 'bar'
echo "\n";
This solution is quite limited but maybe it will work for you

To add/change how classes behave at runtime, you should use Decorators and/or Strategies. This is the prefered OO approach over resorting to any magic approaches or monkey patching.
A Decorator wraps an instance of a class and provides the same API as that instance. Any calls are delegated to the wrapped instance and results are modified where needed.
class Decorator
{
// …
public function __construct($decoratedInstance)
{
$this->_decoratedInstace = $decoratedInstance;
}
public function someMethod()
{
// call original method
$result = $this->_decoratedInstance->someMethod();
// decorate and return
return $result * 10;
}
// …
}
For Strategy, see my more complete example at Can I include code into a PHP class?
More details and example code can be found at
http://sourcemaking.com/design_patterns/decorator
http://sourcemaking.com/design_patterns/strategy

I have a few methods for you to try. :) Have fun coding.
Method for only one class:
class main_class {
private $_MODS = array(),...;
public ...;
public function __construct(...) {
...
global $MODS_ENABLED;
$this -> $_MODS = $MODS_ENABLED;
}
...
public function __get( $var ) {
foreach ( $this->_MODS as $mod )
if ( property_exists( $mod, $var ) )
return $mod -> $var;
}
public function __call( $method, $args ) {
foreach ( $this->_MODS as $mod )
if ( method_exists( $mod, $method ) )
return call_user_method_array( $method, $mod, $args );
}
}
Method for when you want to deal with more than one class:
class modMe {
private $_MODS = array();
public function __construct__() {
global $MODS_ENABLED;
$this -> $_MODS = $MODS_ENABLED;
}
public function __get( $var ) {
foreach ( $this->_MODS as $mod )
if ( property_exists( $mod, $var ) )
return $mod -> $var;
}
public function __call( $method, $args ) {
foreach ( $this->_MODS as $mod )
if ( method_exists( $mod, $method ) )
return call_user_method_array( $method, $mod, $args );
}
}
class mainClass extends modMe {
function __construct(...){
$this -> __construct__();
}
}
Now lets try to use them:
$MODS_ENABLED = array();
$MODS_ENABLED[] = new mod_mail();
$myObj = new main_class(...);
$myObj -> mail("me#me.me","you#you.you","subject","message/body","Extra:Headers;More:Headers");
# Hey look, my mail class was just added into my main_class for later use.
Note:
I am currently using the first method (I only have one class, the mods are exceptions) in my own CMS that I have made from scratch (http://sitegen.com.au), and it works great, my reason on needing this is because I have my main_class that is getting generated after I have required all mods in ./mods-enabled/* creating functions and changing how other functions work, I will also come back here another time with a solution for two mods to both change a function without one winning as it ran first. I have split my plugins in two, mods that run on every site, and plugins that have settings for a site and may not even be enabled.
Have fun programming.

You can extend the class
class foo {
public function a() {
return 'b';
}
}
class woo extends foo {
public function newStuff() {
$var = $this->a();
echo $var;
}
}
By extending foo from the woo class the functionality in foo is usable while you can also create new methods in woo. That's the easiest way to add new functionality to a class.

You can use magic functionality of PHP to provide actions on methods that are not defined at compile time.

It is actually possible. For instance:
<?php
class Test {
function set($set, $val) {
$this->$set = $val;
}
function get($get) {
return $this->$get;
}
}
$t = new Test();
$t->set('hello', 'world');
echo $t->get('hello');
exit;
?>

If it is not enough magic for you, you can use dynamic objects. The common idea is here: https://github.com/ptrofimov/jslikeobject

Related

How to create runtime function which uses object context?

I am having a class and I have a dynamically created function (created via "create_function") but I cannot find a way to tell PHP that I want this function to be created for this class only (class function) and because of that the new function cannot access the object properties. Take a look at the following code:
class Test {
private $var=1;
function __construct() {
call_user_func(create_function('', 'echo $this->var;'));
}
}
new Test;
This throws error "Fatal error: Using $this when not in object context in D:\WWW\index.php(7) : runtime-created function on line 1"
You probably want runkit_method_add, not create_function.
As of php 5.4 anonymus function also have $this in their context. With a little help from the magic _call method it is possible to add a closure as a method to a class, without additional code:
class Test
{
private $var = 1;
function __construct()
{
$this->sayVar = function() { echo $this->var; };
}
public function __call( $method, $args )
{
if ( property_exists( $this, $method ) ) {
if ( is_callable( $this->$method ) ) {
return call_user_func_array( $this->$method, $args );
}
}
}
}
$test = new Test();
$test->sayVar(); // echos 1

PHP dynamic class extending

I know you can extend a class when constructing it like the following:
class b extends a {
}
But is it possible to dynamically extend classes from the scripts? Such as:
$b = new b($input) extends a;
What I wish to accomplish is to extend the module differnetly wheither it's used in admin rather than the public pages. I know I can create two different parent classes by the same name and only include one per admin or public. But my question is, is it possible to do it dynamically in PHP?
No, not without an extension like RunKit.
You might consider an alternative approach. If you want B to assume the functionality of A, perhaps something like the following could provide a sort of "mixin" approach. The general picture is that, instead of B being a child of A, B delegates to A.
<?php
class MixMeIn
{
public $favouriteNumber = 7;
public function sayHi() {
echo "Hello\n";
}
}
class BoringClass
{
private $mixins = array();
public function mixin($object)
{
$this->mixins[] = $object;
}
public function doNothing() {
echo "Zzz\n";
}
public function __call($method, $args)
{
foreach ($this->mixins as $mixin)
{
if (method_exists($mixin, $method))
{
return call_user_func_array(array($mixin, $method), $args);
}
}
throw new Exception(__CLASS__ + " has no method " + $method);
}
public function __get($attr)
{
foreach ($this->mixins as $mixin)
{
if (property_exists($mixin, $attr))
{
return $mixin->$attr;
}
}
throw new Exception(__CLASS__ + " has no property " + $attr);
}
public function __set($attr, $value)
{
foreach ($this->mixins as $mixin)
{
if (property_exists($mixin, $attr))
{
return $mixin->$attr = $value;
}
}
throw new Exception(__CLASS__ + " has no property " + $attr);
}
}
// testing
$boring = new BoringClass();
$boring->doNothing();
try {
$boring->sayHi(); // not available :-(
}
catch (Exception $e) {
echo "sayHi didn't work: ", $e->getMessage(), "\n";
}
// now we mixin the fun stuff!
$boring->mixin(new MixMeIn());
$boring->sayHi(); // works! :-)
echo $boring->favouriteNumber;
Just a zany idea. I hope I understood the question correctly.
You can't, but this has been requested for a few years: https://bugs.php.net/bug.php?id=41856&edit=1
You can define the classes within an eval, but it's more trouble than declaring the class normally.
But you cannot use extends while object creation. extends is used in class definition only and defines which other class is "parent" for our new class.
Alternatively, if you are comfortable with javascript-style inheritance and don't mind losing typechecking:
<? //PHP 5.4+
final class ExpandoLookalike {
//Allow callable properties to be executed
public function __call($name, $arguments) {
\call_user_func_array($this->$name, $arguments);
}
}
$newBaseModule = static function(){
$base = new ExpandoLookalike();
//Common base functions get assigned here.
$basePrivateVar = 42;
$base->commonFunction = static function($params1, $params2) use ($basePrivateVar){
echo "common function\n";
};
$base->comment = static function() use ($basePrivateVar){
echo "Doing base comment with $basePrivateVar\n";
};
return $base;
};
//Javascript-style extends
$newAdminModule = static function($param) use ($newBaseModule){
$base = $newBaseModule();
$privateVar = 5;
$base->adminProperty = 60;
$base->suspendSite = static function() use ($param, $privateVar){
echo 'Doing admin only function ';
echo "with $param, $privateVar\n";
};
return $base;
};
$newPublicModule = static function() use ($newBaseModule){
$base = $newBaseModule();
$privateVar = 3;
//Javascript-style overloading
$oldComment = $base->comment;
$base->comment = static function($data) use ($oldComment, $privateVar){
$oldComment();
echo 'Doing public function ';
echo "with $data\n";
};
return $base;
};
$baseModule = $newBaseModule();
$adminModule = $newAdminModule('P');
$publicModule = $newPublicModule();
$adminModule->suspendSite(); //echos 'Doing admin only function with P, 5'
echo "{$adminModule->adminProperty}\n"; //echos '60'
$publicModule->comment('com'); //echos 'Doing base comment with 42'
//'Doing public function with com'
?>
Despite closing the door to traits and interfaces, it opens up other interesting doors to compensate:
<? //PHP 5.4+
$inheritAllTheThings = static function(){
$base = new ExpandoLookalike();
foreach(\func_get_args() as $object){
foreach($object as $key => $value){
//Properties from later objects overwrite properties from earlier ones.
$base->$key = $value;
}
}
return $base;
};
$allOfEm = $inheritAllTheThings(
$newPublicModule(),
$newAdminModule('Q'),
['anotherProp' => 69,]
);
$allOfEm->comment('f'); //echos 'Doing base comment with 42'
//Because AdminModule came after PublicModule, the function that echos 'f'
//from PublicModule was overridden by the function from AdminModule.
//Hence, order denotes resolutions for multiple inheritance collisions.
$allOfEm->suspendSite(); //echos 'Doing admin only function with Q, 5'
echo $allOfEm->anotherProp . "\n"; //echos '69'
?>
You can with typecasting. If a extends b then you could do
$a=(a)(new b($input));
Which isn't exactly the same, but similar.
You can look: https://github.com/ptrofimov/jslikeobject
Author implemented JS-like objects with support of inheritance.
But perhaps it is not so good to use such objects instead of usual ones.
Yes, as cory mentioned, this feature has been requested before. But before that, you can create a workaround. Here is my old school trick for this
Create two separate classes like these:
class a {
}
class b {
public $object;
}
Then, create an extended version too
class bextendeda extends a {
}
In the constructor method of class b, place few functions which redirects to the extended object if requested.
class b {
public $object;
public function __contruct($extend = false) {
if($extend) $this -> object = new bextendeda();
else $this -> object = $this;
}
function __get($prop) {
return $this-> object -> $prop;
}
function __set($prop, $val) {
$this-> object -> $prop = $val;
}
function __call($name, $arguments)
{
return call_user_func_array(array($this -> object, $name), $arguments);
}
}
And there you have it, IF you want the extended version just do this
$b = new b(true);
If not
$b = new b();
Enjoy :)

Is it possible to use mixins in php

I came to know about mixins.So my doubt is, is it possible to use mixins in php?If yes then how?
Use Trait introduced in PHP 5.4
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
which prints Hello World!
http://php.net/manual/en/language.oop5.traits.php
This answer is obsolete as of PHP 5.4. See Jeanno's answer for how to use traits.
It really depends on what level of mixins you want from PHP. PHP handles single-inheritance, and abstract classes, which can get you most of the way.
Of course the best part of mixins is that they're interchangeable snippets added to whatever class needs them.
To get around the multiple inheritance issue, you could use include to pull in snippets of code. You'll likely have to dump in some boilerplate code to get it to work properly in some cases, but it would certainly help towards keeping your programs DRY.
Example:
class Foo
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
class Fizz
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
It's not as direct as being able to define a class as class Foo mixin Bar, but it should get you most of the way there. There are some drawbacks: you need to keep the same parameter names and return variable names, you'll need to pass other data that relies on context such as func_get_args_array or __FILE__.
Mixins for PHP (PHP does not implement Mixins natively, but this library will help)
First google result for "php5 mixin": http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html
First google result for "php mixin": http://www.advogato.org/article/470.html
Short answer: yes, but not natively (yet, evidently, as #mchl notes). Check those out.
Longer answer: if you're using runkit, checkout runkit_method_copy(): "Copies a method from class to another."
I based mixins functionality on the blog entry found at jansch.nl.
class Node
{
protected $__decorator_lookup = array();
public function __construct($classes = array())
{
foreach($classes as $class)
if (class_exists($class))
{
$decorator = new $class($this);
$methods = get_class_methods($decorator);
if (is_array($methods))
foreach($methods as $method)
$this->__decorator_lookup[strtolower($method)] = $decorator;
}
else
trigger_error("Tried to inherit non-existant class", E_USER_ERROR);
}
public function __get($name)
{
switch($name)
{
default:
if ($this->__decorator_lookup[strtolower($name)])
return $this->__call($name);
}
}
public function __call($method, $args = array())
{
if(isset($this->__decorator_lookup[strtolower($method)]))
return call_user_func_array(array($this->__decorator_lookup[strtolower($method)], $method), $args);
else
trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
}
public function __clone()
{
$temp = $this->decorators;
$this->decorators = array();
foreach($temp as $decorator)
{
$new = clone($decorator);
$new->__self = $this;
$this->decorators[] = $new;
}
}
}
class Decorator
{
public $__self;
public function __construct($__self)
{
$this->__self = $__self;
}
public function &__get($key)
{
return $this->__self->$key;
}
public function __call($method, $arguments)
{
return call_user_func_array(array($this->__self, $method), $arguments);
}
public function __set($key, $value)
{
$this->__self->$key = $value;
}
}
class Pretty extends Decorator
{
public function A()
{
echo "a";
}
public function B()
{
$this->b = "b";
}
}
$a = new Node(array("Pretty"));
$a->A(); // outputs "a"
$a->B();
echo($a->b); // outputs "b"
EDIT:
As PHP clone is shallow, added __clone support.
Also, bear in mind that unset WON'T work (or at least I've not managed to make it work) within the mixin. So - doing something like unset($this->__self->someValue); won't unset the value on Node. Don't know why, as in theory it should work. Funny enough unset($this->__self->someValue); var_dump(isset($this->__self->someValue)); will produce correctly false, however accessing the value from Node scope (as Node->someValue) will still produce true. There's some strange voodoo there.

Emulate public/private properties with __get() and __set()?

I was writing a class that uses __get() and __set() to store and retrieve array elements in a master array. I had a check to make some elements ungettable, basically to re-create private properties.
I noticed that it seemed that __get intercepts all calls to class properties. This sucks for me, because I wanted to have a variable private to the outside world ( unavailable via get ), but I was trying to access it by directly referencing the master array from within the class. Of course, the master array is not in the whitelist of gettable properties :(
Is there a way I can emulate public and private properties in a php class that uses __get() and __set()?
Example:
<?
abstract class abstraction {
private $arrSettables;
private $arrGettables;
private $arrPropertyValues;
private $arrProperties;
private $blnExists = FALSE;
public function __construct( $arrPropertyValues, $arrSettables, $arrGettables ) {
$this->arrProperties = array_keys($arrPropertyValues);
$this->arrPropertyValues = $arrPropertyValues;
$this->arrSettables = $arrSettables;
$this->arrGettables = $arrGettables;
}
public function __get( $var ) {
echo "__get()ing:\n";
if ( ! in_array($var, $this->arrGettables) ) {
throw new Exception("$var is not accessible.");
}
return $this->arrPropertyValues[$var];
}
public function __set( $val, $var ) {
echo "__set()ing:\n";
if ( ! in_array($this->arrSettables, $var) ) {
throw new Exception("$var is not settable.");
}
return $this->arrPropertyValues[$var];
}
} // end class declaration
class concrete extends abstraction {
public function __construct( $arrPropertyValues, $arrSettables, $arrGettables ) {
parent::__construct( $arrPropertyValues, $arrSettables, $arrGettables );
}
public function runTest() {
echo "Accessing array directly:\n";
$this->arrPropertyValues['color'] = "red";
echo "Color is {$this->arrPropertyValues['color']}.\n";
echo "Referencing property:\n";
echo "Color is {$this->color}.\n";
$this->color = "blue";
echo "Color is {$this->color}.\n";
$rand = "a" . mt_rand(0,10000000);
$this->$rand = "Here is a random value";
echo "'$rand' is {$this->$rand}.\n";
}
}
try {
$objBlock = & new concrete( array("color"=>"green"), array("color"), array("color") );
$objBlock->runTest();
} catch ( exception $e ) {
echo "Caught Exeption $e./n/n";
}
// no terminating delimiter
$ php test.php
Accessing array directly:
__get()ing:
Caught Exeption exception 'Exception' with message 'arrPropertyValues is not accessible.' in /var/www/test.php:23
Stack trace:
#0 /var/www/test.php(50): abstraction->__get('arrPropertyValu...')
#1 /var/www//test.php(68): concrete->runTest()
#2 {main}.
Is there a way I can emulate public and private properties in a php class that uses __get() and __set()?
Not directly (if you discount debug_backtrace).
But you can have a private method getPriv that does all the work your current __get does. Then __get would only wrap this private method and check accessibility.
function __get($name) {
if (in_array($name, $this->privateProperties))
throw new Exception("The property ". __CLASS__ . "::$name is private.");
return $this->getPriv($name);
}
Inside your class, you would call getPriv, thus bypassing __get.
Make abstraction::$arrPropertyValues protected or do what Artefacto wrote (if you need additional checks), except that abstraction::getPriv() should be protected.
Rather than manually enlisting private/protected properties, you could use PHPs cumbersome reflection methods:
function __get($name) {
$reflect = new ReflectionObject($this);
$publics = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
if (in_array($name, $publics)) {
return $this->{$name};
}
}

Why can't I overload constructors in PHP?

I have abandoned all hope of ever being able to overload my constructors in PHP, so what I'd really like to know is why.
Is there even a reason for it? Does it create inherently bad code? Is it widely accepted language design to not allow it, or are other languages nicer than PHP?
You can't overload ANY method in PHP. If you want to be able to instantiate a PHP object while passing several different combinations of parameters, use the factory pattern with a private constructor.
For example:
public MyClass {
private function __construct() {
...
}
public static function makeNewWithParameterA($paramA) {
$obj = new MyClass();
// other initialization
return $obj;
}
public static function makeNewWithParametersBandC($paramB, $paramC) {
$obj = new MyClass();
// other initialization
return $obj;
}
}
$myObject = MyClass::makeNewWithParameterA("foo");
$anotherObject = MyClass::makeNewWithParametersBandC("bar", 3);
You can use variable arguments to produce the same effect. Without strong typing, it doesn't make much sense to add, given default arguments and all of the other "work arounds."
For completeness, I'll suggest Fluent Interfaces. The idea is that by adding return $this; to the end of your methods you can chain calls together. So instead of
$car1 = new Car('blue', 'RWD');
$car2 = new Car('Ford', '300hp');
(which simply wouldn't work), you can do:
$car = (new Car)
->setColor('blue')
->setMake('Ford')
->setDrive('FWD');
That way you can pick exactly which properties you want to set. In a lot of ways it's similar to passing in an array of options to your initial call:
$car = new Car(['make' => 'Ford', 'seats' => 5]);
PHP Manual: Function Arguments, Default Values
I have overcome this simply by using default values for function parameters. In __constuct, list the required parameters first. List the optional parameters after that in the general form $param = null.
class User
{
private $db;
private $userInput;
public function __construct(Database $db, array $userInput = null)
{
$this->db = $db;
$this->userInput = $userInput;
}
}
This can be instantiated as:
$user = new User($db)
or
$user = new User($db, $inputArray);
This is not a perfect solution, but I have made this work by separating parameters into absolutely mandatory parameters no matter when the object is constructed, and, as a group, optional parameters listed in order of importance.
It works.
True overloading is indeed unsupported in PHP. As #Pestilence mentioned, you can use variable arguments. Some people just use an Associative Array of various options to overcome this.
they say this work:
<?php
class A
{
function __construct()
{
$a = func_get_args();
$i = func_num_args();
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array(array($this,$f),$a);
}
}
function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
function __construct2($a1,$a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
function __construct3($a1,$a2,$a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');
// results:
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
?>
and, it seem every one are happy with it, but for me it didn't work...
if you get it to work, its one kind of overloading too...
it take all argoments and pass them to the secondary function constructor...
<?php
//php do not automatically call parent class constructor at all if child class has constructor so you have to call parent class constructor explicitly, however parent class constructor is called automatically if child class has no constructor
class MyClass
{
function construct1($value1)
{
echo "<br/> dummy constructor is called with 1 arguments and it is $value1";
}
function construct2($value1,$value2)
{
echo "<br/> dummy constructor is called with 2 arguments and it is $value1, $value2";
}
function construct3($value1,$value2,$value3)
{
echo "<br/> dummy constructor is called with 3 arguments and it is $value1, $value2 , $value3";
}
public function __construct()
{
$NoOfArguments = func_num_args(); //return no of arguments passed in function
$arguments = func_get_args();
echo "<br/> child constructor is called $NoOfArguments";
switch ($NoOfArguments) {
case 1:
self::construct1($arguments[0]);
break;
case 2:
self::construct2($arguments[0],$arguments[1]);
break;
case 3:
self::construct3($arguments[0],$arguments[1],$arguments[2]);
break;
default:
echo "Invalid No of arguments passed";
break;
}
}
}
$c = new MyClass();
$c2 = new MyClass("ankit");
$c2 = new MyClass("ankit","Jiya");
$c2 = new MyClass("ankit","Jiya","Kasish");
?>
You can use conditional statements in your constructor and then perform your task.
Eg.
class Example
{
function __construct($no_of_args)
{// lets assume 2
switch($no_of_args)
{
case 1:
// write your code
break;
case 2:
//write your 2nd set of code
break;
default:
//write your default statement
}
}
}
$object1 = new Example(1); // this will run your 1st case
$object2 = new Example(2); // this will run your 2nd case
and so on...
You can of course overload any function in PHP using __call() and __callStatic() magic methods.
It is a little bit tricky, but the implementation can do exactly what your are looking for.
Here is the resource on the official PHP.net website:
https://www.php.net/manual/en/language.oop5.overloading.php#object.call
And here is the example which works for both static and non-static methods:
class MethodTest
{
public function __call($name, $arguments)
{
// Note: value of $name is case sensitive.
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}
/** As of PHP 5.3.0 */
public static function __callStatic($name, $arguments)
{
// Note: value of $name is case sensitive.
echo "Calling static method '$name' "
. implode(', ', $arguments). "\n";
}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context'); // As of PHP 5.3.0
And you can apply this to constructors by using the following code in the __construct():
$clsName = get_class($this);
$clsName->methodName($args);
Pretty easy.
And you may want to implement __clone() to make a clone copy of the class with the method that you called without having the function that you called in every instance...
Adding this answer for completeness with respect to current PHP , since later versions of PHP , you can in fact overload constructors in a way . Following code will help to understand ,
<?php
class A
{
function __construct()
{
$a = func_get_args();
$i = func_num_args();
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array(array($this,$f),$a);
}
}
function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
function __construct2($a1,$a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
function __construct3($a1,$a2,$a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');
?>
Output :
__construct with 1 param called: sheep
__construct with 2 params called: sheep,cat
__construct with 3 params called: sheep,cat,dog
In this case I recommend using Interfaces:
interface IExample {
public function someMethod();
}
class oneParamConstructor implements IExample {
public function __construct(private int $someNumber) {
}
public function someMethod(){
}
}
class twoParamConstructor implements IExample {
public function __construct(private int $someNumber, string $someString) {
}
public function someMethod(){
}
}
than in your code:
function doSomething(IExample $example) {
$example->someMethod();
}
$a = new oneParamConstructor(12);
$b = new twoParamConstructor(45, "foo");
doSomething($a)
doSomething($b)
As far as I know, constructor overloading in PHP is not allowed, simply because the developers of PHP did not include that functionality - this is one of the many complaints about PHP.
I've heard of tricks and workarounds, but true overloading in the OOP sense is missing. Maybe in future versions, it will be included.
I think we can also use constructor with default arguments as a potential substitute to constructor overloading in PHP.
Still, it is really sad that true constructor overloading is not supported in PHP.
<?php
class myClass {
public $param1 = 'a';
public $param2 = 'b';
public function __construct($param1 = NULL, $param2 = NULL) {
if ($param1 == NULL && $param2 == NULL) {
// $this->param1 = $param1;
// $this->param2 = $param2;
} elseif ($param1 == NULL && $param2 !== NULL) {
// $this->param1 = $param1;
$this->param2 = $param2;
} elseif ($param1 !== NULL && $param2 == NULL) {
$this->param1 = $param1;
// $this->param2 = $param2;
} else {
$this->param1 = $param1;
$this->param2 = $param2;
}
}
}
// $myObject = new myClass();
// $myObject = new myClass(NULL, 2);
$myObject = new myClass(1, '');
// $myObject = new myClass(1, 2);
echo $myObject->param1;
echo "<br />";
echo $myObject->param2;
?>
public function construct1($user , $company)
{
dd("constructor 1");
$this->user = $user;
$this->company = $company;
}
public function construct2($cc_mail , $bcc_mail , $mail_data,$user,$company)
{
dd('constructor 2');
$this->mail_data=$mail_data;
$this->user=$user;
$this->company=$company;
$this->cc_mail=$cc_mail;
$this->bcc_mail=$bcc_mail;
}
public function __construct()
{
$NoOfArguments = func_num_args(); //return no of arguments passed in function
$arguments = func_get_args();
switch ($NoOfArguments) {
case 1:
self::construct1($arguments[0]);
break;
case 5:
self::construct2($arguments[0],$arguments[1],$arguments[2],$arguments[3],$arguments[4]);
break;
default:
echo "Invalid No of arguments passed";
break;
}
I'm really no OOP expert, but as I understand it overloading means the ability of a method to act differently depending in the parameters it receives as input. This is very much possible with PHP, you just don't declare the input types since PHP does not have strong typing, and all the overloading is done at runtime instead of compile time.

Categories