I am developing on Symfony2 and I need to call a method on a class, both known only at runtime.
I have already successfully used variable functions and call_user_func in the project, but this time they give me problems...
My code looks like this
namespace MyBundleNamespace;
use MyBundle\Some\Class;
class MyClass
{
public static function myFunction() { ... }
}
and in some other file I need to do this
MyClass::myFunction();
but dynamically, so I tried both
$class = "MyClass";
$method = "myFunction";
$class::$method();
and
$class = "MyClass";
$method = "myFunction";
call_user_func("$class::$method");
But I get a class MyClass not found error. Of course the class is included correctly with use and if I call MyClass::myFunction() just like that it works.
I also tried to trigger the autoloader manually like suggested in this question answer comment, but it did not work. Also, class_exists returned false.
What am I missing? Any ideas?
Thanks!
You're missing the namespace:
$class = '\\MyBundleNamespace\\MyClass';
$method = 'myFunction';
Both calls should work:
call_user_func("$class::$method");
call_user_func(array($class, $method));
Related
This is an odd situation and I think the answer is 'you can't do that, what are you thinking?' but hope someone can prove me wrong.
My goal is to store a globally scoped function in a variable then inject it for execution within a class object.
I would like to avoid using call_user_func() as this searches for the function in the global namespace and is the same effect as if I were to just execute the global function from within the class object. I would like my class to execute the object as if it were an internal class method, not an external function. This comes close but not quite.
I cannot modify the function or wrap it in a class.
(Why am I jumping through these hoops?) Needs to be used within this class to follow a spec.
I know I can just duplicate the function in the class and be done with it, but you know the issues with that (plus it creeps up on SRP.) Reflection would work perfectly but this function is not in a class, it is just out there in an include. I've tried wrapping it an anonymous function and the closure object doesn't execute the function.
Is there any hope to do this? The function is simple, accepts a scalar param, does some stuff to it, returns a value (and is tightly coupled with other code, cannot be moved or changed.)
function someFunction($param)
{
// do some stuff
return $someScalarValue;
}
What I would hope is something like
$func = someFunction([some value]); // doesn't work of course, this would store result in $func
$cls = new SomeClass($func);
Then a method in the class could run the function object, much like call_user_func but not have to search the global namespace.
protected function someThing()
{
$this->injected_function([some class value]); // also doesn't work of course
}
When you use $this you are in the objects instance scope. You could pass a (reference) method into the constructor.
$myFunc = function($arg) { var_dump($arg); return 314; };
class myClass {
private $func;
public function __construct($func) {
$this->func = $func;
}
public function do($value) {
$this->func->call($this, $value);
}
}
$var = 'Hello world!';
$myObj = new myClass($myFunc);
$value = $myObj->do($var); // $value is now 314
If you do not want the function to be stored in global namespace you can just pass even an anonymous function like this on the fly:
$myObj = new myClass(function($arg) { var_dump($arg); return 314; });
$value = $myObj->do($var); // $value is now 314
Thank you #Markkus Zeller for your comments, as I suspected there is no way to do what I originally was tasked, to "inject" a global function as an dependency. There is, but it only really works with anonymous functions.
After a lot of stressful pushback, I convinced our managers that wrapping this in a simple class was the way to go. This,
// require_once('some-function.php');
function someFunction($param)
{
// do some stuff
return $someScalarValue;
}
. . . now becomes this. (Typed out on the fly and may contain deficiencies, concept only)
// require_once('some-function.php');
require_once('path/to/SomeFunctionClass.php');
function someFunction($param)
{
$cls = new SomeFunctionClass($param);
return $cls->execute();
}
. . . where execute() contains identical code that was in someFunction(). I can now use "SomeFunctionClass" for a DI. There is more to the story, but that is the gist, and this one change can be implemented without modifying any of the 450 or so instances that use this global function (and each of those they can be gradually ported to use the new wrapper.) It also allows me to isolate and mock the functionality for unit testing.
I have a called class called ClientPolicy which is like this
class ClientPolicy {
var $serverHost="www.example.com";
var $httpPort = 80;
var $httpsPort = 443;
var $appKey;
var $secKey;
var $defaultContentCharset = "UTF-8";
}
and another class file name SyncAPIClient which looks like this
class SyncAPIClient{
function SyncAPIClient(ClientPolicy $clientPolicy) {
$this->clientPolicy = $clientPolicy;
}
function SyncAPIClient($appKey, $appSecret) {
$this->clientPolicy = new ClientPolicy();
$this->clientPolicy->appKey=$appKey;
$this->clientPolicy->secKey=$appSecret;
}
}
My questions are
1.) If you check the function in SyncAPIClient, you will notice that the ClientPolicy class was passed as a parameter before a variable, what does it really mean? What is the essence of passing a class in function parameter?
2.) I am getting an error "Cannot redeclare SyncAPIClient::SyncAPIClient()" in my script log and the reason is that SyncAPIClient function was called twice in SyncAPIClient class. How can I solve this issue? Is there any better way to write this SyncAPIClient function instead of passing it twice?
The author of this script is nowhere to be found and I am left to fix it.
1) Here the $clientPolicy variable that is passed to this function, needs be a ClientPolicy instance.
In this way, if the argument that is passed is different from an instance of ClientPolice class, an error is generated.
function SyncAPIClient(ClientPolicy $clientPolicy) {
$this->clientPolicy = $clientPolicy;
}
https://wiki.php.net/rfc/typed_properties_v2
https://laravel-news.com/php7-typed-properties
2) The error Cannot redeclare SyncAPIClient::SyncAPIClient() is caused because you are trying to declare two functions called SyncAPIClient ().
If in first SyncAPIClient() method you just want save the $clientPolicy in $this->clientPolicy, you can use the magic method __construct. Or just try changing the name of one of the functions, and the problem should be a problem.
class SyncAPIClient{
__construct(ClientPolicy $clientPolicy) {
$this->clientPolicy = $clientPolicy;
}
function SyncAPIClient($appKey, $appSecret) {
$this->clientPolicy = new ClientPolicy();
$this->clientPolicy->appKey=$appKey;
$this->clientPolicy->secKey=$appSecret;
}
}
https://www.php.net/manual/en/language.oop5.decon.php
http://www.zentut.com/php-tutorial/php-constructor-and-destructor/
Hope this helps!
I would've fix the code you have like this:
class SyncAPIClient
{
private $clientPolicy = null;
function SyncAPIClient(ClientPolicy $clientPolicy = null)
{
if($clientPolicy instanceof ClientPolicy){
$this->clientPolicy = $clientPolicy;
}else{
$this->clientPolicy = new ClientPolicy();
}
}
public function setAuthParams($appKey, $appSecret) {
$this->clientPolicy->appKey=$appKey;
$this->clientPolicy->secKey=$appSecret;
}
}
This way you can instantiate a SyncAPIClient with or without a ClientPolicy.
Without ClientPolicy:
$syncAPI = new SyncAPIClient();
$syncAPI->setAuthParams($apiKey, $apiSecret);
With ClientPolicy:
$clientPolicy = new ClientPolicy();
$clientPolicy->appKey=$appKey;
$clientPolicy->secKey=$appSecret;
$syncAPI = new SyncAPIClient($clientPolicy);
When using class and functions in combination like
Rtin::
Functions nested inside that class Rtin should have different names than that class name
So you shouldn't have function called rtin
However you can call function from outside the class with it's name
From the error you have may be due to:
function you nested in the class or the function outside the class has a duplicate outside the script itself. Like having function mentioned in included function.php file and also mentioned in the script itself so php get confused because function name is written in two php files at the same time
Example of class
class Rtin{
private $data;
private $results;
public function getResultsType(){
return ........
}
}
To call class use
$q = Rtin::getResultsType($data['name']);
In your example. Adapt it to the example I have provide and review the included files for duplicate function .
I have a class file: we'll call it class.php. The functionality of that is to grab info from an ini file (yeah, I posted the question about security and was given the great suggestion to use either a config file or an ini file to hold the DB information).
Essentially, my class is this:
<?php
class myClass
{
public function getAttached()
{
$file = "../../myFile.ini";
if (!$settings = parse_ini_file($file, TRUE)) throw new exception('Unable to open ' . $file . '.');
$hoost = $settings['mysqli']['default_host'];
$useer = $settings['mysqli']['default_user'];
$pazz = $settings['mysqli']['default_pw'];
$dbs = $settings['mysqli']['default_db'];
$con = mysqli_connect($hoost ,$useer, $pazz, $dbs);
return $con;
}
}
$obj = new myClass();
$obj->getAttached();
$vals = $obj->getAttached();
//echo $vals; //didn't know if I should echo this or not.
?>
I want to call this in my somePage.php file to make my "mysqli" connection and go from there...
I tried this:
require_once('class.php');
getAttached();
Obviously that didn't work (I knew it wouldn't but - I did it anyway just to see if "maybe"), so - how do I call that function from my class file in the regular php page?
Any thoughts would be appreciated.
Thanks in advance.
You need to make an instance of the class before calling the functions as they're not static.
require_once('class.php');
$myClass = new myClass();
$myClass-> getAttached();
or, like I said above you could make the function static.
public static function myFunction() {
//etc...
}
Then to call it you would use:
require_once('class.php');
myClass::getAttached();
You have to instanciate your class first, the same way you did it in you class.php file:
$myclass = new myClass();
$myClass->getAttached();
Note that if your method can be used without any relation with your class, you could make it static:
public static function getAttached() {
// ...
}
And use it without having to instanciate your class:
myClass::getAttached();
Your getAttached() method within the myClass ,create the instance for the class and call
the function
$call = new myClass();
$call->getAttached();
Given answers are correct, but if you keep your class file as you posted, you have object already in $obj so there is no need to make new one. If it is just temporary you can ignore my post.
One more thing:
$obj->getAttached(); // this line is not needed, as you call this function in next line
$vals = $obj->getAttached();
If I declared a class in a controller and want to use it in a model without passing the class' pointer, how can I redeclare that class without the "Fatal error: Class already declared"? If I use the get_declared_classes() function, I see that the class is declared, but how can I get the pointer to that class so that I can use it in the model?
Basically, how can I use a class that's been declared but with no pointer.
Any help would be greatly appreciated.
Thanks in advance!
EDIT: Maybe the word "pointer" was misused. Here's some code
// Controller...one file
$class = new Class();
$model = $this->load_model('example.php');
$model->dosomething();
// Model...example.php
function dosomething() {
// I want to access the class here. Is it only possible to do this by
// passing a $class parameter to the function or can I do it without
// passing it as a variable?
}
I think you're mixing terminology. There's no concept of a pointer anywhere in PHP. References are similar concepts, but that's another topic.
What I think you're trying to do, is use a variable to indicate the class in the model. So, you can use a string. So let's say you want to tell the model to use class Foo, you could inject the class name into the model:
$model = new Model('foo');
Then, inside the constructor:
public function __construct($class) {
$this->className = $class;
}
Then, when you want to use it, just call new:
$class = $this->className;
$obj = new $class();
But note that it has nothing to do with object scope. So you could do it anywhere:
$class = 'Foo';
$obj = new $class;
How would I get something like this to work?
$class_name = 'ClassPeer';
$class_name::doSomething();
Depending on version of PHP:
call_user_func(array($class_name, 'doSomething'));
call_user_func($class_name .'::doSomething'); // >5.2.3
To unleash the power of IDE autocomplete and error detection, use this:
$class_name = 'ClassPeer';
$r = new \ReflectionClass($class_name );
// #param ClassPeer $instance
$instance = $r->newInstanceWithoutConstructor();
//$class_name->doSomething();
$instance->doSomething();
Basically here we are calling the static method on an instance of the class.
Use call_user_func. Also read up on PHP callbacks.
call_user_func(array($class_name, 'doSomething'), $arguments);
These answers are all outdated:
<?php
class MyTest{
public static function bippo(){
echo "hello";
}
}
$a = MyTest::class;
$a::bippo();
works fine
After I have almost missed the simplest solution from VolkerK, I have decided to extend and put it in a post. This is how to call the static members on the instance class
// calling class static method
$className = get_class($this);
$result = $className::caluclate($arg1, $arg2);
// using class static member
foreach ($className::$fields as $field) {
:
}
Reflection (PHP 5 supports it) is how you'd do this. Read that page and you should be able to figure out how to invoke the function like that.
$func = new ReflectionFunction('somefunction');
$func->invoke();
Documentation Link
if you need to adjust the namespace
$call = call_user_func(array('\\App\\Models\\'.$class_name, "doSomething"));