Asked  7 Months ago    Answers:  5   Viewed   33 times

I have a class which I need to use to extend different classes (up to hundreds) depending on criteria. Is there a way in PHP to extend a class by a dynamic class name?

I assume it would require a method to specify extension with instantiation.

Ideas?

 Answers

26

I don't think it's possible to dynamically extend a class (however if I'm wrong I'd love to see how it's done). Have you thought about using the Composite pattern (http://en.wikipedia.org/wiki/Composite_pattern, http://devzone.zend.com/article/7)? You could dynamically composite another class (even multiple classes - this is often used as a work around to multiple inheritance) to 'inject' the methods/properties of your parent class into the child class.

Wednesday, March 31, 2021
 
Markol
answered 7 Months ago
36

Since $className is static and within the parent class, when you set className within A or B, it changes the variable within the parent, and the same is done when the variable is read. Unless you override className in your extended classes, you'll be storing and retrieving information from the same memory location, originally defined in InstanceModule.

If you redefine className in A/B, you can access className using parent:: or self:: from InstanceModule or A/B respectively. Depending on what you are trying to do, Abstract classes may also play a significant role.

See Static Keyword or Class Abstraction on the PHP5 Manual.

Wednesday, March 31, 2021
 
Valdas
answered 7 Months ago
34

This is advanced behavior which will not be needed in 90+% of the enumerations created.

According to the docs:

The rules for what is allowed are as follows: _sunder_ names (starting and ending with a single underscore) are reserved by enum and cannot be used; all other attributes defined within an enumeration will become members of this enumeration, with the exception of __dunder__ names and descriptors (methods are also descriptors).

So if you want a class constant you have several choices:

  • create it in __init__
  • add it after the class has been created
  • use a mixin
  • create your own descriptor

Creating the constant in __init__ and adding it after the class has been created both suffer from not having all the class info gathered in one place.

Mixins can certainly be used when appropriate (see dnozay's answer for a good example), but that case can also be simplified by having a base Enum class with the actual constants built in.

First, the constant that will be used in the examples below:

class Constant:  # use Constant(object) if in Python 2
    def __init__(self, value):
        self.value = value
    def __get__(self, *args):
        return self.value
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)

And the single-use Enum example:

from enum import Enum

class Planet(Enum):
    MERCURY = (3.303e+23, 2.4397e6)
    VENUS   = (4.869e+24, 6.0518e6)
    EARTH   = (5.976e+24, 6.37814e6)
    MARS    = (6.421e+23, 3.3972e6)
    JUPITER = (1.9e+27,   7.1492e7)
    SATURN  = (5.688e+26, 6.0268e7)
    URANUS  = (8.686e+25, 2.5559e7)
    NEPTUNE = (1.024e+26, 2.4746e7)

    # universal gravitational constant
    G = Constant(6.67300E-11)

    def __init__(self, mass, radius):
        self.mass = mass       # in kilograms
        self.radius = radius   # in meters
    @property
    def surface_gravity(self):
        return self.G * self.mass / (self.radius * self.radius)

print(Planet.__dict__['G'])             # Constant(6.673e-11)
print(Planet.G)                         # 6.673e-11
print(Planet.NEPTUNE.G)                 # 6.673e-11
print(Planet.SATURN.surface_gravity)    # 10.44978014597121

And, finally, the multi-use Enum example:

from enum import Enum

class AstronomicalObject(Enum):

    # universal gravitational constant
    G = Constant(6.67300E-11)

    def __init__(self, mass, radius):
        self.mass = mass
        self.radius = radius
    @property
    def surface_gravity(self):
        return self.G * self.mass / (self.radius * self.radius)

class Planet(AstronomicalObject):
    MERCURY = (3.303e+23, 2.4397e6)
    VENUS   = (4.869e+24, 6.0518e6)
    EARTH   = (5.976e+24, 6.37814e6)
    MARS    = (6.421e+23, 3.3972e6)
    JUPITER = (1.9e+27,   7.1492e7)
    SATURN  = (5.688e+26, 6.0268e7)
    URANUS  = (8.686e+25, 2.5559e7)
    NEPTUNE = (1.024e+26, 2.4746e7)

class Asteroid(AstronomicalObject):
    CERES = (9.4e+20 , 4.75e+5)
    PALLAS = (2.068e+20, 2.72e+5)
    JUNOS = (2.82e+19, 2.29e+5)
    VESTA = (2.632e+20 ,2.62e+5

Planet.MERCURY.surface_gravity    # 3.7030267229659395
Asteroid.CERES.surface_gravity    # 0.27801085872576176

Note:

The Constant G really isn't. One could rebind G to something else:

Planet.G = 1

If you really need it to be constant (aka not rebindable), then use the new aenum library [1] which will block attempts to reassign constants as well as Enum members.


1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

Thursday, June 3, 2021
 
nomie
answered 5 Months ago
48

You can put it into the constructor like this:

public function __construct() {
    $this->fullname  = $this->firstname.' '.$this->lastname;
    $this->totalBal  = $this->balance+$this->newCredit;
}

Why can't you do it the way you wanted? A quote from the manual explains it:

This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.

For more infromation about OOP properties see the manual: http://php.net/manual/en/language.oop5.properties.php

Friday, June 4, 2021
 
koenHuybrechts
answered 5 Months ago
59

Yes, it is possible, that is know as variable functions, have a look at this.

Example from PHP's official site:

<?php
class Foo
{
    function Variable()
    {
        $name = 'Bar';
        $this->$name(); // This calls the Bar() method
    }

    function Bar()
    {
        echo "This is Bar";
    }
}

$foo = new Foo();
$funcname = "Variable";
$foo->$funcname();  // This calls $foo->Variable()

?>

In your case, make sure that the function do_the_thing exists. Also note that you are storing the return value of the function:

$req = $class->$function_name();

Try to see what the variable $req contains. For example this should give you info:

print_r($req); // or simple echo as per return value of your function

Note:

Variable functions won't work with language constructs such as echo(), print(), unset(), isset(), empty(), include(), require() and the like. Utilize wrapper functions to make use of any of these constructs as variable functions.

Friday, September 24, 2021
 
Good Person
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :