PHP5 Class scope quirks - php

Hey php gurus. I'm running into some bizarre class scope problems that clearly have to do with some quirk in php. Can anyone tell me what out-of-the-ordinary situations might give the following error...
Fatal error: Cannot access self:: when no class scope is active in MyClass.php on line 5
Now, obviously if I were to use self:: outside of the class, I'd get errors... but I'm not. Here is a simplified version of the situation...
//file1
class MyClass{
public static function search($args=array()){
$results = MyDbObject::getQueryResults("some query");
$ordered_results = self::stack($results); //Error occurs here
return $ordered_results;
}
public static function stack($args){
//Sort the results
return $ordered_results;
}
}
//file 2
include_once("MyClass.php");
$args = array('search_term'=>"Jimmy Hoffa");
$results = MyClass::search($args);
given this setup how can I get the error above? Here is what I've found so far...
MyClass::search($args) //does not give the error (usually)
call_user_func("MyClass::search"); // this gives the error!
Any other situations?

If I understand correctly, you are looking for Late Static Binding. This feature requires PHP version 5.3 at least.

You're not passing any parameters, but your method is looking for them. Try
call_user_func("MyClass::search", $args);
This works in php 5.3.1, but call_user_func("MyClass::search"); doesn't

Try this:
call_user_func(array('MyClass', 'search'));
See also example #4 on http://php.net/call_user_func

Your code seems fine. If there's something wrong with it, I must be missing the problem. It appears that your call to self:: is totally within the scope of a class! And a static scope, specifically, which is what self:: is for.
From the 3rd Edition of PHP Objects Patterns and Practice (an awesome book):
To access a static method or property from within the same class
(rather than from a child), I would use the self keyword. self is to
classes what the $this pseudo-variable is to objects. So from outside
the StaticExample class, I access the $aNum property using its class
name:
StaticExample::$aNum;
From within the StaticExample class I can use the self keyword:
class StaticExample {`
static public $aNum = 0;
static public function sayHello() {
self::$aNum++;
print "hello (".self::$aNum.")\n";
}
}
So, I am not sure why this code was failing. Perhaps a PHP bug? I came upon this error when actually trying to use self:: outside of the scope of a class-- my error looked like this:
public static function get_names() {
$machine_names = self::get_machine_names();
return array_map(function ($machine_name) {
$service_settings = self::get_settings_by_machine_name($machine_name);
return $service_settings . $machine_name;
},
$machine_names
);
}
So, I get the error because I use self:: within the scope of the closure. To fix the error, I could make that call to self::get_settings_by_machine_name() before the closure, and pass the results to the closure's scope with use.
Not sure what was happening in your code.

Related

Purpose of Static Class Functions in PHP

I thought that the use of the static keyword in the declaration of a class function meant that you could call the function without an instance of the class using the scope resolution operator (::).
For example:
class Foo
{
public static function static_function() {
return 'x';
}
public function non_static_function() {
return 'y';
}
}
// to call static_function:
echo Foo::static_function();
// to call non_static_function:
$foo = new Foo();
echo $foo->non_static_function();
The PHP.net documentation on static seems to support this idea.
I came across some code yesterday that someone had wrote accessing class functions using the scope resolution operator that had NOT been defined with the static keyword. I was surprised and confused to see this worked.
Given my class defined above, Foo, it turns out you can actually do:
echo Foo::static_function();
echo Foo::non_static_function();
Resulting in output xy without generating any errors or warnings.
If you can access non-static class functions without the static keyword, what is the point in it?
Resulting in output xy without generating any errors or warnings.
No way. The error reporting must have been turned off on that environment. That would have definitely produced a Strict Standards warning notice.

Difference in Class Calling PHP

With learning fuelPHP, I am introduced on calling classes using scope resolution, or :: in sense. Typically, when we call a method in a class we do this ...
$myclass = new myclass();
$myclass->mymethod();
On fuel, methods are usually called in this manner ...
myclass::mymethod();
I was wondering if there are any difference between the two? Is the scope resolution is something of an update on 5.3 as well ... if not, which one is ideal, or when should I use these.
Thanks.
The scope resolution operator is used to access either class constants like ::const, static variables like ::$var or call static methods like ::method().
See http://php.net/manual/en/language.oop5.static.php
Static methods can be called without having an instance of the class they are defined in. They're defined in that class with the static keyword.
For example, one of CakePHP's static methods is defined like this:
class ClassRegistry {
// ...
public static function &getInstance() {
// ...
}
}
... which you can call like ClassRegistry::getInstance().
Without the static keyword, you'd need an instance of the ClassRegistry class to call that function.
You can read more here, especially about why using static methods in your own code can sometimes be a bad idea: http://kore-nordmann.de/blog/0103_static_considered_harmful.html
I am not sure how would myclass::mymethod(); work, since I use such syntax only when I am calling a STATIC class.
MyClass::DoSomething();
would call a static method named DoSomething()
while
$instance = new MyClass();
$instance->DoSomething();
would call the instance method.
I have not tested it but I believe you will run into an error if you do $instance::DoSomething()
I think the best way to understand why there is a static call and what it does behind the scene is to check this FuelPHP blog's entry: http://fuelphp.com/blog/2011/05/why-did-you-do-that
The obvious difference is that the first solution $myObject->myMethod() it's a dynamic call : you need an instance to execute myMethod().
In the second solution, MyClass::myMethod() is a static call. The class acts as a sort of namespace where a function belong. You don't need an instance for that.

PHP create_function Instance variable - Unable to call anonymous function: Follow up

This is somewhat a follow up to a previous question - but I've distilled the question down and have the "works" vs. "doesn't work" cases narrowed down much more precisely.
My Goal:
I have a class MyClass that has an instance variable myFunction. Upon creating a MyClass object (instantiating), the constructor assigns the instance variable myFunction with the result of a call to create_function (where the code and args come from a db call).
Once this object of type MyClass is created (and stored as an instance variable of another class elsewhere) I want to be able to call myFunction (the instance variable anonymous function) from "anywhere" that I have the MyClass object.
Experimental Cases -- below is my highly simplified test code to illustrate what works vs. what doesn't (i.e. when the expected functionality breaks)
class MyClass extends AnotherClass {
public $myFunction;
function __construct() {
$functionCode = 'echo "NyanNyanNyan";';
$this->myFunction();
/*Now the following code works as expected if put in here for testing*/
$anonFunc = $this->myFunction;
$anonFunc(); //This call works just fine (echos to page)!
/*And if i make this call, it works too! */
self::TestCallAnon();
}
public function TestCallAnon() {
$anonFunc2 = $this->myFunction;
$anonFunc2();
}
}
However, if I do the following (in another file, it errors saying undefined function () in... within the Apache error log.
//I'm using Yii framework, and this is getting the user
//objects instance variable 'myClass'.
$object = Yii::app()->user->myClass;
$object->TestCallAnon(); // **FAILS**
or
$func = $object->myFunction;
$func(); // ** ALSO FAILS **
In addition, several variations of calls to call_user_func and call_user_func_array don't work.
If anyone is able to offer any insight or help that would be great :).
Thanks in advance!
You can't pass references to functions around in PHP like you can in for instance JavaScript.
call_user_func has limited functionality. You can use it like so:
class MyClass {
function func() {}
static function func() {}
}
function myfunc() {}
$i = new MyClass();
call_user_func("myfunc", $args);
call_user_func(array($i, "func"), $args);
call_user_func(array(MyClass, "staticFunc"), $args);
I ended up solving this issue via a workaround that ended up being a better choice anyways.
In the end I ended up having a static class that had a method to randomly return one of the possible identifiers, and then another method which accepted that identifier to build the anonymous function upon each class.
Slightly less elegant than I would like but it ends up working well.
Thanks to everyone for your efforts.

PHP passing a class name becomes a string when passed to a function

Here's what I want to do:
public function all($model) {
$query = 'SELECT ' . implode(', ', $model::$fields) ....;
}
Called like this:
$thing->all(Account);
I get this error:
Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM in /home/mark/public_html/*/account.php on line 15
When inspecting $model with var_dump it turns out its a string. In the the first example if I change $model to Account on the $query line it works fine.
How can a take a string and turn it back into a class?
Edit: Updated example and title to reflect the problem isn't with self.
Solution: Since I'm not using PHP5.3, I had to resort to using eval() to get what I wanted. Thanks everybody!
Classes are not first-class citizens in PHP, as such they may not be stored in variables, passed as function arguments, or returned from functions.
However, PHP will let you simulate a first-class citizen by using a string containing the name of the class, in certain situations:
$class = "Account";
$instance = new $class(); // You can create instances
call_user_func(array($class, 'frobnicate')); // You can call static functions
That's about all in PHP < 5.3. However, with PHP 5.3, you can also:
$class::frobnicate(); // cleanly call static functions
$fields = $class::$fields; // access static variables
I have also experienced receiving such Fatal Error: Class 'MyClass' not found when you're class has a specific namespace, then it's probably the namespacing. You need to also mention the namespace in your String variable.
$class = "App\MyClass"; // mention the namespace too
$instance = new $class();
See Wikipedia on the scope resolution operator. Especially see the section on PHP and Hebrew.
You cannot use self this way : it can only be used in a static context (i.e. inside a static method) to point to the class -- and not its name.
If you are working with non-static methods (seems you are), you should use $this, instead of self.
Actually, before PHP 5.3, you cannot use a static method/data with a "dynamic" (i.e contained in a variable) class name -- see the examples on the page Static Keyword : they only work with PHP 5.3, for that kind of manipulation.
Which means a portion of code like this one :
class ClassA {
public static $data = 'glop';
}
$className = 'ClassA';
var_dump($className::$data);
Will not work with PHP < 5.3
I find this similar line works in my Laravel app:
$thing->all(new Account);
Try '$this' instead of self.
Self does work this way in PHP. PHP thinks it encounters an unknown constant, which it can't find, and then it assumes it's a string containing 'self'.
Edit: Can you post the class and the code where you instantiate the object?

PHP Callable Object as Object Member

I have a class Logger which, among other things has a method Log.
As Log is the most common use of the Logger instance, I have wired __invoke to call Log
Another class, "Site" contains a member "Log", an instance of Logger.
Why would this work:
$Log = $this->Log;
$Log("Message");
But not this:
$this->Log("Message");
The former fails with "PHP Fatal error: Call to undefined method Site::Log()"
Is this a limitation of the callable object implementation, or am I misunderstanding something?
Unfortunately, this is (still) a limitation of PHP, but it makes sense when you think about it, as a class can contain properties and methods that share names. For example:
<?php
class Test {
public $log;
public function __construct() {
$this->log = function() {
echo 'In Test::log property';
};
}
public function log() {
echo 'In Test::log() method';
}
}
$test = new Test;
$test->log(); // In Test::log() method
call_user_func($test->log); // In Test::log property
?>
If PHP were to allow the syntax you desire, which function would be invoked? Unfortunately, that only leaves us with call_user_func[_array]() (or copying $this->log to another variable and invoking that).
However, it would be nice if the following syntax was acceptable:
<?php
{$test->log}();
?>
But alas, it is not.
Same reasons you can't do this:
$value = $this->getArray()["key"];
or even this
$value = getArray()["key"];
Because PHP syntax doesn't do short hand very well.
This may work:
${this->Log}("Message");
But perhaps it's just easier and better to use the full call? There doesn't seem to be a way to get what you want to work on the one line.
The error in your question indicates it is looking for a function defined on the class which doesn't exist. An invokable object isn't a function, and it seems it can't be treated as one in this case.
as of php7.4 the following code works for me
($this->Log)("Message");

Categories