I know the following could potentially create problems elsewhere and is probably bad design, but I still would like to know why this fails (for my own edification):
class Test {
// singleton
private function __construct(){}
private static $i;
public static function instance(){
if(!self::$i){
self::$i = new Test();
}
return self::$i;
}
// pass static requests to the instance
public static function __callStatic($method, $parameters){
return call_user_func_array(array(self::instance(), $method), $parameters);
}
private $someVar = 1;
public function getSomeVar(){
return $this->someVar;
}
}
print Test::getSomeVar();
The error is Using $this when not in object context
Obviously $this is unavailable from a static method, but the static method is handing it off to an instance method invokation via call_user_func_array, which should make $this the instance...
/EDIT
I'm aware that $this is not available in static context. However, this works:
print call_user_func_array(array(Test::instance(), 'getSomeVar'), array());
Which is exactly what's happening in the __callStatic overload, so something's amiss...
/EDIT 2
scope is definitely getting handled strangely. if you pull the singleton instance to any other class, it works as expected:
class Test {
// singleton
private function __construct(){}
private static $i;
public static function instance(){
if(!self::$i){
self::$i = new Blah();
}
return self::$i;
}
// pass static requests to the instance
public static function __callStatic($method, $parameters){
return call_user_func_array(array(static::instance(), $method), $parameters);
}
}
class Blah {
private $someVar = 1;
public function getSomeVar(){
return $this->someVar;
}
}
print Test::getSomeVar();
You can not staticfy your object methods through __callStatic calls in PHP. It will only be invoked when the method so far does not exist (incl. not being visible from the calling context). In your case Test::getSomeVar() is already defined.
Because of backwards compatibility, PHP does not check if only a static method exists, but actually if generally a method exists.
In your case you are calling the non-static method statically, so $this is not defined because __callStatic has not been invoked. If you would have enabled warnings and notices to the highest level (recommended for development), PHP would have warned you about that.
The correct usage therefore is:
echo Test::instance()->getSomeVar();
As with any other implementation of a Singleton.
So you are just using __callStatic wrong, it only works for methods not yet defined. Choose another tool/design for the job, like the aggregation example you use with your Blah class.
A further note:
You should normally prevent to use any static context in PHP anyway, especially as you exploit a lot of magic functionality here, too, which is another smell. It looks like you have a pile of design issues before you even finished your code. You will only increase the likelihood to run into hard to debug and maintain code with all this. But that just so you do not say in some time you have not been warned.
See Who needs singletons? in case you want to learn more.
It doesn't work like that. In a static call you have no instance, no object and no $this. Call static only works with static methods in which $this is unavailable.
The documentation says:
__callStatic() is triggered when invoking inaccessible methods in a static context.
I know this is not the answer you were hoping for. As you have anticipated: you don't need of overloading in PHP, just create your app and leave this thing out.
Related
I need a class that would let the user take advantage of both normal (non-static) methods and static methods. The actual implementation would reside in the static methods, which would be called by virtual non-static methods of the same name via the magic __call method. The problem however is that, when calling a non-static method in this way, PHP thinks that the programmer made a mistake and the static method was actually meant to be called:
class AClass
{
public static function method ()
{
// implementation
}
public function __call ($name, $arguments)
{
var_dump("Hello");
call_user_func_array("AClass::$name", $arguments);
}
}
$obj = new AClass;
$obj->method();
So "Hello" is no being output when invoking method method on the object but is being output if trying to invoke a method with any other name. Any way to achieve the correct behavior while preserving method naming?
I don't really see why you would try to do that but this seems to work :
class AClass
{
protected static function method ()
{
echo "method()\n";
}
public function __call ($name, $arguments)
{
echo("Hello normal\n");
call_user_func_array("self::$name", $arguments);
}
public static function __callStatic($name, $arguments)
{
echo("Hello static\n");
call_user_func_array("self::$name", $arguments);
}
}
$obj = new AClass;
// Normal call
$obj->method();
// Static call
AClass::method();
Output :
Hello normal
method()
Hello static
method()
The thing is, accordingly to its documentation, the __call method is only used if a function is inaccessible from the context you called it from.
So since in your case the function was public, there was no need to use it (even if with some settings it displays a warning).
If you make it "protected" or "private", it can't be called from the outside and then __call is used.
Because __call doesn't care about static calls, you also have to use __callStatic (available in PHP >=5.3) if you still need to call AClass::method from outside your class.
Assuming that I have to create a class that takes some text do some processing and return it ... with no dependency and it's a stateless class..
I'd like to know would be better to create a stateless class without constructor or just create a static class (in php it's just Static methods)
class like this:
class ClassName
{
public function processText($text)
{
// Some code
$text = $this->moreProcessing($text);
return $text;
}
protected function moreProcessing($text)
{
return $text;
}
}
and this:
class ClassName
{
public static function processText($text)
{
// Some code
$text = static::moreProcessing($text);
return $text;
}
protected static function moreProcessing($text)
{
return $text;
}
}
I Know that dependency injection into the class where these classes are used would be better but assume that I just won't have dependency injection..
My question is mainly would it be better to create static class for the simple example above?
Practically you will see no difference whatsoever.
It's only in the syntax, and the ability of a constructor to perform stuff automatically, though you still have to create instances to invoke the constructor, which in this case is not far off calling some equivalent static member function.
However, non-static member functions are supposed to affect internal state so, if you have no state, static member functions seem more conventional, and will be slightly less surprising to users of the class.
The best approach, though, is to stick your functions in a namespace. Classes are for data and functions operating on that data... even static ones.
So in PHP you can have
Class A{
function B(){}
}
and you can call this as if it were a static function:
A::B();
My question is...if I can do this, then why should I ever declare the function B() as static since doing so makes $this unavailable, so there's less flexibility, so you have everything to lose but nothing to gain...
or is there an advantage of declaring the function as static that I'm not aware of?
also I heard that "static calling of non static methods" are "deprecated"....what does that exactly mean especially in relation to this scenario? is calling A::B() when B() is not declared static something that I shouldn't be doing? if so, why is that the case?
Because PHP tends to be a bit loosy-goosy around strictness (?) these sort of things work. The fact that they are deprecated means that sometime in a future release, it is likely not to work anymore. So if you are calling a non-static function in a static context, your program may break in a future PHP upgrade.
As for using it right now - the advantage to declaring a function as static is that you are deciding right there how that function should be used. If you intend to use a function in a static context, you can't use $this anyway, so you are better of just being clear on what you plan to do. If you want to use the function statically, make it static. If you don't, then don't. If you want to use a function both statically and non-statically, then please recheck your requirements :P
For compatibility mode. Now calling non-static methods statically generates an E_STRICT level warning.
Why static and not instantiate the object? Each programmer will tell you a reason. I particulary preffer instantiate object than use static methods. It's clear, traceable and more reusable.
I did a test bench and the difference was minimal between instantiate and call a method than call it staticaly.
Tip: if you foresee calling methods statically defines them as well;-)
First off, you couldn't do stuff like that in your post in a strict typed language like Java. Java code doesn't compile, if you call non-static stuff in a static context. PHP is not that strict on these things (yet), but still you shouldn't do things just because you can, although it's bad practice and in some languages even 'incorrect' practice.
There sure are advantages using static methods. And it's not quite right that you gain nothing or even lose flexibility. Let's have an example:
class A {
private static $prop_a = 'property_a';
public static function b() {
echo 'called b()';
echo self::$prop_a;
$A = new A();
$A->c();
}
public function c() {
echo 'called c()';
}
}
Now we can call the class this way:
A::b();
which outputs
called_b
property_a
called_c
But you can do the same with:
$a = new A();
$a->b();
$a->c();
c() is executed twice now, but you get the idea. Within your class, you can instanciate the class itself and work with it like with a regular object. But from outside, it's simply one line of code while it's 3 lines using the non-static way. Pretty cool, huh?
And as you see, you can use the static function in a non-static context, which means, you can declare your method static, but if you instanciate your class, you can simply call it like a regular method. Sounds pretty flexible to me ;)
And no, you can't use $this in a static context, but that's what self is for ;)
If it is a static function you don't have to instantiate the class in order to use the method.
Class A {
static function hello($arg)
{
echo 'Hello, ' . $arg;
}
}
A::hello('world');
VS
Class A {
public function hello($arg)
{
echo 'Hello, ' . $arg;
}
}
$a = new A;
$a->hello('world');
The important thing here is the instantiation of the object. After you've instantiated an object, it can be manipulated, and calling your methods may yield unexpected results. If your values and functions are statically declared in the class, they cannot be modified at the time you call the function.
This is not to say they you should always use static methods either. take the following example.
class My_math {
static function pi()
{
return 3.14
}
}
class My_bakery {
private var $pie;
function set_pie($pie)
{
$this->pie = $pie;
}
function pie()
{
echo "I made you a " . $this->pie . "pie";
}
}
I've made My_Math return a constant value, because I know pi doesn't change. But in My_bakery, some days I want blueberry pie and some days I want peach pie.
I'm creating a dynamic class that responds to magic __call() method. The problem is, since I'm building this on top of a already existing framework (Kohana), it checks if the method of the class exists using ReflectionClass::hasMethod, and it doesn't seem to trigger the __call() magic method for checking for it's existance. What could I do in this case? Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it
Without more details, I'm unsure if this would suffice, however you could create proxy class to perform intermediate functionality:
class MyProxy {
protected $_object = null;
protected $_methods = array();
public function __construct($object) {
if (!is_object($object)) {
throw new InvalidArgumentException('$object must be an object');
}
$this->_object = $object;
}
public function __call($name, $arguments) {
return $this->callMethod($name, $arguments);
}
public function setMethod($name, Closure $method) {
$this->_methods[(string) $key] = $method;
}
public function callMethod($name, array $arguments) {
if (isset($this->_methods[$name])) {
return call_user_func_array($this->_methods[$name], $arguments);
}
return call_user_func_array(array($this->_object, $name), $arguments);
}
}
By calling $proxy->setMethod('foo', function () { });, you can dynamically "attach" methods to the object. When you call $proxy->foo(), it'll first do a look-up against the dynamically attached methods; if it finds one, it'll call it. Otherwise, it'll just delegate to the internal object.
Now, the problem with this approach is that attached methods aren't bound to the proxy. In other words, $this doesn't exist in the scope of an attached method.
This can be fixed though, with features from PHP 5.4+.
public function setMethod($name, Closure $method) {
$this->_methods[(string) $name] = Closure::bind($method, $this);
}
We've refined setMethod to rebind the passed closure to the proxy. Now, in the scope of an attached method, $this will point to the proxy object.
We could have rebound it to the enclosed object, but then the attached methods couldn't talk to the proxy (or other attached methods). For completeness, you'll want to add __get and __set magic, to forward property access/mutate calls to the internal object (or handle them in the proxy, whatever)
Besides, the internal object has no clue about the proxy, so it's methods (from the class definition) won't know about any of this dynamic magic anyway.
Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it
Right, as you're creating a new property, not a method. At time of writing, PHP did not support calling anonymous functions in properties without going through __call, which isn't Reflection-friendly. Properties-with-anonymous-functions-as-methods work properly in PHP 5.4.
The only other way to add a method to a class in a way that Reflection will pick up on is using the highly experimental "runkit" extension.
I am using PHP 5.2
I have the following code:
class MyClass {
public function __construct() {}
public static function stuff() {
echo 'This is static! <br />';
}
}
$myClass = new MyClass();
MyClass::stuff(); // Reference by class.
$myClass->stuff(); // Reference by instance of class.
The output works in both cases here is the output:
This is static!
This is static!
Is there a problem using the 2nd way of referencing versus the 1st?
Since I am not allowed to have a non-static function with the same signature as the static one above that won't be an issue. I want the function to be static because there is also a speed boost when using static functions.
Am I missing anything or is the only issue here regarding the semantics of how the -> dereference syntax does not indicate this is a static function?
The docs explicitly say it's okay:
A property declared as static can not
be accessed with an instantiated class
object (though a static method can).
However, it's clearer to use ::. I also question the idea that the static method is significantly faster, particularly when no instance fields are used. You should do profiling before you start altering the semantics of your application for performance.