1

SO,

I have a problem with concept of PHP class method calling. Let we have a class that implementing simple sigma action, for example, a sum:

class Sigma
{
   protected $_fValue = null;

   public function __construct($fValue)
   {
      $this->_fValue = (double)$fValue;
   }

   public static function __callStatic($sName, $rgArgs)
   {
      if(count($rgArgs)!=count(array_filter($rgArgs, function($mArg)
      {
         return is_object($mArg) && $mArg instanceof self;
      })))
      {
         throw new Exception('Can not call method "'.$sName.'": invalid arguments');
      }
      if(!count($rgArgs))
      {
         return null;
      }
      $rResult = array_shift($rgArgs);
      foreach($rgArgs as $mArg)
      {
         $rResult = $rResult->$sName($mArg);
      }
      return $rResult;
   }

   public function getValue()
   {
      return $this->_fValue;
   }

   public function getSigma(self $rSigma)
   {
      return new self($this->getValue()+$rSigma->getValue());
   }

}

As you can see, we can use it's getSigma() method to return an instance of itself that represents simple sum of current object and input object:

$rFirst  = new Sigma(-4.5);
$rSecond = new Sigma(5);
$rThird  = $rFirst->getSigma($rSecond);
var_dump($rThird->getValue());//float(0.5) 

Well, everything is ok here. But as you can see, I've defined __callStatic() method in my class.

What I want to achieve - is that if I'll call my getSigma() method via static call, I would be able to pass any number of parameters since my __callStatic() method is expected to be triggered. But, unfortunately, this is not happening. Instead of this, PHP calls my getSigma() method directly as it was defined as static:

$rFirst  = new Sigma(-4.5);
$rSecond = new Sigma(5);
$rFourth = Sigma::getSigma($rFirst, $rSecond);//Fatal error: Using $this when not in object context in ...

And I understand why fatal error was triggered - PHP calls non-static method via :: operator - then, inside it , $this is used.

So to make long story short: is there a way to trigger __callStatic() method when non-static method is called as static? Or at least how to prevent that PHP 'bug-or-feature' with calling non-static methods via :: (and static methods via ->)?

All this I need to implement beautiful interface, when I can either use one method name for either object call or class call. And, since that is my goal, I can not make my non-static methods private or protected (i.e. they should be accessible for using outside)

8
  • Maybe reading the manual helps? "The overloading methods are invoked when interacting with properties or methods that have not been declared or are not visible in the current scope. " That is directly from the page of __callStatic. So make it private, route it, add the other __call magic method and you can differ yourself. Commented Sep 9, 2013 at 8:14
  • @hakre actually, not. The question is about handling a case - when we have non-static method and calling it as a static (i.e. we have no instance, but somehow PHP calls that method - there's no context at all for doing that since there's no instance). Now I have an answer (and that includes explanation about how PHP stores methods in opcode - so I can understand why this 'bug-or-feature' is happening) Commented Sep 9, 2013 at 8:18
  • No, you used the __callStatic feature for a visible method. This does not work and you wrote you needed to google a lot, however actually this is written right on the page in the PHP manual. That's all I just commented and it still is the case. As the feature itself is - per the docs - the wrong tool, you can not expect it to work. This makes all additional discussion about some imaginated "context" somewhat moot as no such exists. Commented Sep 9, 2013 at 8:53
  • Regarding "but somehow PHP calls that method": Yes, that is PHP 4 backwards compatibility. However if you have proper warning settings, you're notified about the problem in your code. Let me find a related question so that is better to understand. Commented Sep 9, 2013 at 8:56
  • But I'm not arguing with that. The question is not about this. It's about - how to avoid this. I'm aware that it's happening and so I was seeking way to resolve this matter. Also for me was not (till this question - it's about somehow) clear - how PHP deals with magic method calling conditions, which are ambiguous: on one hand, there is a visible method, on the other hand - there's no corresponding static method. That's it. Commented Sep 9, 2013 at 8:56

2 Answers 2

1

Unfortunately not. As long as there is a method with the name you are trying to call, PHP will call it without considering type of call vs the type of method.

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

2 Comments

Yep, that's an answer I've got after having some time with googling and reading SO. A shame. Sometimes I hate OOP model in PHP
@AlmaDoMundo: What, you hate only the OO model? ;-)
0

Maybe it could solved by implementing __call. Like this:

public function __call($name, $args) {
    if (method_exists($this, "_$name") {
        call_user_func_array(array($this, "_$name"), $args); 
    } else {
    // no method found
    }
}

private function _getSigma($somearg) {
// ...
}

edit

So if you need class to extend or to implement something, try using somekind of wrapper, where you can mangle calls:

class SigmaWrapper {

    private static $_defaultClass = 'Sigma';
    private $_sigmaObject = null;

    public static function factory($sigmaObject) {
        // now would be good to implement some factory   
    }    

    private function setSigmaObject($obj) {
        $this->_sigmaObject = $arg;
    }

    public function __construct($arg) {
        if (is_object($arg)) {
          $this->setSigmaObject($arg);
        } else {
            $class = self::$_defaultClass;
            $this->_sigmaObject = new {$class}($fValue);
        }
    }

    public function __call($name, $args) {
        if (method_exists($this->_sigmaObject, "$name")) {
            // do whatever you need
            // call_user_func_array(array($this->_sigmaObject), "$name"), $args); 
        } else {
            // do whatever you need
            //call
        }
    }

    public function __callStatic($name, $args) {
        if (method_exists(get_class($this->_sigmaObject), "$name")) {
            // do whatever you need
            // call_user_func_array(array(get_class($this->_sigmaObject), "$name"), $args); 
        } else {
            // do whatever you need
            //call
        }
    }

    public function __get($name) {
        return $this->_sigmaObject->{$name};
    }

    public function __set($name, $value) {
        $this->_sigmaObject->{$name} = $value;
    }
}

5 Comments

No. That will not helps - as I need to have directly declared getSigma() method: my class is implementing some interface which contains declaration of getSigma() method
@Alma Do Mundo: Your class it not implementing that interface, otherwise you wouldn't expect the same method to be static and non-static. That would violate the interface. So consider where you make the difference, but you can't have non-difference and difference at once. Think about it a little. Changing the interface might help (e.g. make it a strategy?)
@hakre not sure what do you mean. here is the sample. Yes, there is an interface. I just wanted to extend my possibilities via described in question methods calling. Your argument is wrong here. I expect that static method and non-static are not the same (the best will be a case when I can declare both methods - with the same name, but first as static and second as non-static. Since it's not possible in PHP, I've tried that way in question)
The interface has public function getSigma($rSigma) - this is a non-static method. You want this method as well static. This is violating the interface. Something is wrong: Either the interface or the class.
No, I want to provide another way to call it. (Remember - I'm treating static and non-static methods as a different entities and so, it's not a violation, it's extension - since you will be able to call non-static method as it was defined in interface)

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.