2

consider the following:

class A
{
    public static function bark()
    { echo 'woof'; }
}

class B extends A
{
    public static function speak()
    { echo 'hello'; }
}

A::speak();

// Fatal error: Call to undefined method A::speak()

How is one supposed to extend a class with methods that you need to be globally available within that class with methods that are not yet known, but are loaded at run-time, depending on the flow of your application?

Yes sure we can make traits and put use in the class like:

trait B
{
    public static function speak()
    { echo 'hello'; }
}

class A
{
    use B;
    public static function bark()
    { echo 'woof'; }
}

A::speak();

// hello
  • but then use B is not called dynamically, hence you will have to update class A with every new trait available - manually. This is absurd, why force developers to break their brain in trying to accomplish something so fundamentally simple?

Does anyone have an idea how this can be done in a clean way? I mean I have seen some impressive methods by using Singletons, namespaces, callbacks and the works, but in each case it requires a lot more code and repetitive programming than what is really needed. Either that or i'm missing the boat completely haha! Thanks in advance, your help will be appreciated and voted generously.

11
  • 2
    In short, because B extends A it inherits As methods but A does not extend B so it doesn't inherit Bs methods; therefore B::bark() will work, A::speak() won't - as you've found. This is only an issue with static methods really because they belong to the class rather than any objects instantiated from that class. Commented Mar 5, 2015 at 14:13
  • I know that "using globals" is frowned upon, and I agree, but it is relative to how it is used. In this case - grouping "helper functions" specifically to what is needed, not loading everything all at once "in case it is needed". With that being said, the properties and methods in classes can be private and finally declared, effectively "protecting" the class and its methods. Furthermore, the system can be written with a "user privilege system" that only allows usage of such classes per user, and, an application can be a user itself, and it can all be contained in namespaces respectively. Commented Mar 5, 2015 at 14:13
  • 1
    There is a place for static methods - beyond simply "using globals". Say for instance you make an Email class and have some methods in there for validation or whatever. It makes sense for the validation methods to be instance methods as they apply to the email data held in that object. Now, say you want to add a blacklist that will apply to every email object ever instantiated. In that case it makes sense to use a static method. With the static method the blacklist will be held in memory once, for the class, with an instance method every Email object will hold the blacklist in memory. Commented Mar 5, 2015 at 14:34
  • 1
    “with methods that are not yet known, but are loaded at run-time, depending on the flow of your application” – I doubt this is actually a good idea … it sounds like the path to a God Object that will do all sorts of things in different contexts – which is usually not desirable. But your A/B and bark/speak “example” is so generic, that it is quite impossible to tell if you are trying to implement something that would actually make sense, or if you are off the right path completely already. Commented Mar 10, 2015 at 1:48
  • 3
    Sounds like a terrible design pattern. There's very likely another way, but it will be hard to tell you what without more specific examples. Commented Mar 10, 2015 at 2:23

1 Answer 1

2
+50

I think with some creativity you could make use of the __call magic method. You could do something like this:

class A
{
    /**
     * @var array
     */
    protected $methods = [];

    public function __call($name, $arguments)
    {
        if (!empty($this->methods[$name]) && is_callable($this->methods[$name])) {
            return call_user_func_array($this->methods[$name], $arguments);
        }
    }

    public function addMethod($name, Closure $method)
    {
        $this->methods[$name] = $method;
    }
}

// get class instance (A have no methods except of addMethod)
$instance = new A();

// add methods on runtime
$instance->addMethod('sum', function($num1, $num2) {
    return $num1 + $num2;
});

$instance->addMethod('sub', function($num1, $num2) {
    return $num1 - $num2;
});

// use methods exactly the same way as implemented fixed on class
echo $instance->sum(2, 2);
echo $instance->sub(3, 2);

Of course you could use also __callStatic for static methods. And if you want to get it a bit more complex also could use the concept of Dependency Injection to add objects instead of methods. Then search the called method through the injected objects and call it when it's found. I hope this give you at least a good idea.

Sign up to request clarification or add additional context in comments.

4 Comments

Excellent idea @David! Will the methods added via addMethod also be available in other methods or functions in the sub-tree without invoking new A() ?
Thanks! Yes, you can use __callStatic the same way to add and use static methods.
It's great because with your pattern here, it would be easy to raise a user exception if something is about to be overwritten, or one can even re-route a call to a method that is prohibited for a certain user-group, and serve an appropriate response. Vote++!!
Exactly, you can do anything you want. It's completely dynamic and very simple. Good to know this help :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.