Helo everyone.
I have a class MyClass and a function escape() that can be called as a static class or as an instantiated Object.
MyClass::_escape('..... some string...');
or
$myclass->escape();
What I would like is not to have the underscore on the staic version and for both just have the same function definition. I trie to do.
class MyClass {
public $_string = "";
public function escape($string = null) {
if($string == null)
return new String(mysql_real_escape_string($this->_string));
else
return new String(mysql_real_escape_string($string));
}
}
but this function fails by the PHP parser. Is there a way of doing what I attempted to above??
So to summarise, I would like the static call to look like;
print Myclass::escape('some string');
and the instantiated call to look like;
print $myobject->escape(); //Which escapes the private variable _string
Hope this was clear.
regards
public function _escape($s){
return self::escape($s);
}
What you're trying to achieve won't work without at least some kind of error:
Example using static:
error_reporting(E_ALL ^ E_STRICT);
class MyClass
{
// note the *static* keyword
public static function escape($string = null) {
// $this is not defined, even if called as object-method
var_dump(isset($this));
}
}
$foo = new MyClass();
$foo->escape(); // => bool(false)
MyClass::escape(); // => bool(false)
So, if you remove the static keyword and try again, you'll get:
$foo->escape(); // => bool(true)
but also:
Strict Standards: Non-static method MyClass::escape() should
not be called statically ...
for
MyClass::escape(); // => bool(false)
There are no parse errors in the code you posted. In fact, it works just as you want it to work, as long as you never pass $string to the escape() method in an object context:
$foo = new MyClass();
$foo->_string = 'foo';
$foo->escape(); // This works.
MyClass::escape('bar'); // This works, too.
$foo->escape('baz'); // Don't do this. It'll escape $string instead of $this->_string.
You could resolve this issue by determining whether or not you're in a static context within the escape() method, instead of checking for the existence of $string.
Related
How can I assign a function to a method in a class in PHP? I tried the following:
class Something{
public function __construct(){
$functionNames = array('foo', 'bar')
$variable = 'blablabla';
foreach($functionNames as $functionName){
if(method_exists($this, $functionName))
continue;
$this->{$functionName}() = function($params){ //should create the methods "foo" and "bar"
echo $variable; //should echo 'blablabla' (I know that the variable was declared outside this function, but how can I access it anyway?)
}; //the error points to here
}
}
}
But this code gives me this error:
Fatal error: Can't use method return value in write context
Does anyone know how I can assign the anonymous function to the class method, while also still being able to access variables outside that function?
You are doing foreach($functionNames as $functionName){ which means that $functionName is a string, not an array. So, don't use $functionName[0].
method_exists takes 2 parameters. One is the object and the other is the method name. It should be:
method_exists($this, $functionName)
As for creating the function, you don't need () on the left side of the =. It should be:
$this->$functionName = function($params) use($variable){
echo $variable;
};
The use($variable) is needed to tell PHP to use that variable inside the function. That's how closures work in PHP, it's different than other languages.
So, your class should look like:
class Something{
public function __construct(){
$functionNames = array('foo', 'bar');
$variable = 'blablabla';
foreach($functionNames as $functionName){
if(method_exists($this, $functionName)){
continue;
}
$this->$functionName = function($params) use($variable){
echo $variable;
};
}
}
}
Problem here is that in this way of making functions, you are not actually creating a class method, but instead creating a class variable that contains a function.
So, you need to call it like so:
$test = new Something;
$foo = $test->foo;
$foo('abc');
You can't just do $test->foo('abc');.
EDIT: Another thing you can do is use PHP's __call "magic method". This will be ran whenever you do ->funcName(), regardless of whether the method exists or not. Using that method, you can just check to see if the method called was 'foo' or 'bar'. See this example:
class Something{
private $variable;
public function __construct(){
$this->variable = 'blablabla';
}
public function __call($name, $params=array()){
if(method_exists($this, $name)){
// This makes sure methods that *do* exist continue to work
return call_user_func(array($this, $name), $params);
}
else{
$functionNames = array('foo', 'bar');
if(in_array($name, $functionNames)){
// You called ->foo() or ->bar(), so do something
// If you'd like you can call another method in the class
echo $this->variable;
}
}
}
}
With this, now you can do the following:
$test = new Something;
$test->foo('abc'); // Will echo "blablabla"
I have a class that generates data based on a few things. I would like to format that data from the outside. So I am trying to pass a function into the class so that it would format that data. I have looked at many examples, but it seems this is unique.
Can anybody give an idea of how to do this? The following code gives an error.
<?php
class someClass {
var $outsideFunc; // placeholder for function to be defined from outside
var $somevar='Me'; // generated text
function echoarg($abc){
$outsideFunc=$this->outsideFunc; // bring the outside function in
call_user_func($outsideFunc,$abc); // execute outside function on text
echo $abc;
}
}
function outsidefunc($param){ // define custom function
$param='I am '.$param;
}
$someClass=new someClass();
$someClass -> outsideFunc = 'outsideFunc'; // send custom function into Class
$someClass -> echoarg($someClass->somevar);
$someClass -> outsidefunc = 'outsidefunc';
In PHP, function names are not case sensitive, yet object property names are. You need $someClass->outsideFunc, not $someClass->outsidefunc.
Note that good OOP design practice calls for the use of getter and setter methods rather than just accessing properties directly from outside code. Also note that PHP 5.3 introduced support for anonymous functions.
Yeah. You are right. Now there is no error. But it does not work either.
By default, PHP does not pass arguments by reference; outsidefunc() does not actually do anything useful. If you want it to set $param in the caller to something else, and do not want to just return the new value, you could change the function signature to look like this:
function outsidefunc(&$param) {
You would also need to change the way you call the function, as call_user_func() does not allow you to pass arguments by reference. Either of these ways should work:
$outsideFunc($abc);
call_user_func_array($outsideFunc, array(&$abc));
Why not pass your function as an argument?
<?php
class someClass {
public $somevar="Me";
public function echoarg($abc,$cb=null) {
if( $cb) $cb($abc);
echo $abc;
}
}
$someClass = new someClass();
$someClass->echoarg($someClass->somevar,function(&$a) {$a = "I am ".$a;});
i am not sure what exactly you are looking for, but what i get is, you want to pass object in a function which can be acheive by
Type Hinting in PHP.
class MyClass {
public $var = 'Hello World';
}
function myFunction(MyClass $foo) {
echo $foo->var;
}
$myclass = new MyClass;
myFunction($myclass);
OP, perhaps closures are what you're looking for?
It doesn't do EXACTLY what you're looking for (actually add function to class), but can be added to a class variable and executed like any normal anonymous function.
$myClass->addFunc(function($arg) { return 'test: ' . $arg });
$myClass->execFunc(0);
class myClass {
protected $funcs;
public function addFunc(closure $func) {
$this->funcs[] = $func;
}
public function execFunc($index) { $this->funcs[$index](); } // obviously, do some checking here first.
}
Consider the following code, which is a scheme of storing a callback function as a member, and then using it:
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return $this->callback();
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass(myFunc);
echo $o->makeCall();
I would expect myFunc was here to be echoed, but instead I get:
Call to undefined method MyClass::callback()
Can anyone explain what's wrong here, and what I can do in order to get the desired behaviour?
In case it matters, I am using PHP 5.3.13.
You can change your makeCall method to this:
function makeCall() {
$func = $this->callback;
return $func();
}
Pass it as a string and call it by call_user_func.
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return call_user_func($this->callback);
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass("myFunc");
echo $o->makeCall();
One important thing about PHP is that it recognises the type of a symbol with the syntax rather than the contents of it, so you need to state explicitly what you refer to.
In many languages you just write:
myVariable
myFunction
myConstant
myClass
myClass.myStaticMethod
myObject.myMethod
And the parser/compiler knows what each of the symbols means, because it's aware of what they refer to simply by knowing what's assigned to them.
In PHP, however, you need to use the syntax to let the parser know what "symbol namespace" you refer to, so normally you write:
$myVariable
myFunction()
myConstant
new myClass
myClass::myStaticMethod()
$myObject->method()
However, as you can see these are calls rather than references. To pass a reference to a function, class or method in PHP, combined string and array syntax is used:
'myFunction'
array('myClass', 'myStaticMethod')
array($myObject, 'myMethod')
In your case, you need to use 'myFunc' in place of myFunc to let PHP know that you're passing a reference to a function and not retrieving the value the myFunc constant.
Another ramification is that when you write $myObject->callback(), PHP assumes callback is a method because of the parentheses and it does not attempt to loop up a property.
To achieve the expected result, you need to either store a copy of/reference to the property callback in a local variable and use the following syntax:
$callback = $this->callback;
return $callback();
which identifies it as a closure, because of the dollar sign and the parentheses; or call it with the call_user_func function:
call_user_func($this->callback);
which, on the other hand, is a built-in function that expects callback.
I use eval() in my current project like this:
if (class_exists($class_name)) //$class_name depends on user input
eval($class_name.'::MyStaticMethod()');
eval() is executed if and only if class with the name $class_name exists so it's kinda safe, but I still don't think that this is the best solution.
Can I do the same what code above does without eval()?
I have recently answered this question. The last part of my answer perfectly answers this question and is much more useful for future readers than answers provided here. That's why I am answering my own question.
PHP has features that gives possibility to avoid using eval in most cases:
PHP is very dynamic language. It has ability to do following stuff with strings:
Define and/or get variable (supported from PHP 4.3). For example:
$variableName = 'MyVariable';
// Create new variable with the name defined in variable $variableName
${$variableName} = 'MyValue';
//Outputs: string(7) "MyValue"
var_dump($MyVariable);
//Outputs: string(7) "MyValue"
var_dump(${'MyVariable'});
Demo
Call function (supported from PHP 4.3). For example:
// Create function with the name defined in variable $functionName
function MyFunction($argument) {
return 'Argument passed is: '.$argument;
}
$functionName = 'MyFunction';
// Outputs:
// string(48) "Argument passed is: Calling MyFunction directly."
var_dump(MyFunction('Calling MyFunction directly.'));
// Outputs:
// string(51) "Argument passed is: Calling MyFunction with string."
var_dump($functionName('Calling MyFunction with string.'));
Demo
Create instance of class (supported from PHP 5.0). For example:
class MyClass {
public function __construct() {
echo 'Constructing MyClass'."\n";
}
}
$className = 'MyClass';
$objFromString = new $className();
// Outputs: object(MyClass)#1 (0) {}
var_dump($objFromString);
Demo
Call static method (supported from PHP 5.0). For example:
class MyClass {
public static function staticMethod() {
return 'MyClass::staticMethod called';
}
}
$staticMethodName = 'staticMethod';
// Outputs: string(28) "MyClass::staticMethod called"
var_dump(MyClass::$staticMethodName());
Demo
And from PHP 5.3 class name can also be defined by string. Example:
class MyClass {
public static function staticMethod() {
return 'MyClass::staticMethod called';
}
}
$className = 'MyClass';
$staticMethodName = 'staticMethod';
var_dump($className::$staticMethodName());
var_dump($className::staticMethod());
Demo
Call instance method of object (supported from PHP 5.0). For example:
class MyClass {
public function instanceMethod() {
return 'MyClass::instanceMethod called';
}
}
$methodName = 'instanceMethod';
$obj = new MyClass();
// Outputs: string(30) "MyClass::instanceMethod called"
var_dump($obj->$methodName());
Demo
Access static and instance properties of object (supported from PHP 5.0). For example:
class MyClass {
public static $myStaticProperty;
public $myInstanceProperty;
}
$staticPropertyName = 'myStaticProperty';
$instancePropertyName = 'myInstanceProperty';
MyClass::${$staticPropertyName} = 'my static value';
$obj = new MyClass();
$obj->{$instancePropertyName} = 'my instance value';
var_dump(MyClass::${$staticPropertyName});
var_dump($obj->{$instancePropertyName});
Demo
PHP has two functions: call_user_func and call_user_func_array for dynamic function/method calls. Both are perfectly documented so I won't go in details here.
Even if everything above is not enough PHP 5 comes with great Reflection API. Unfortunately, documentation has few examples but reflection is quite large topic to cover here. Basically, It's not a big deal to use reflection after reading how it works.
I'd suggest call_user_func.
An alternative to call_user_func() would be calling it like this:
$class_and_method = 'Class::MyStaticMethod()';
$class_and_method();
yes:
call_user_func(array($class_name, 'MyStaticMethod'));
As of PHP 5.3+,
$class_name::MyStaticMethod();
Adisory:
userinput + eval = security hole;
Also eval is an expensive operation requiring parsing the string into an actionable format (parse tree, abstract syntax tree, etc.) and executing the new found logic.
You don't want to eval every little tidbit of code. Use eval if you have something for it to chew on or rather put that logic somewhere where it's reusable and parametrized such as a function.
Also as of php 5.4
$method = array('class_name', 'method_name');
$method(); // calls class_name::method_name()
I have been doing some tests (to replace old code) with the __invoke magic method and I'm not sure this is a bug or not:
Lets suppose we have a class:
class Calc {
function __invoke($a,$b){
return $a*$b;
}
}
The following is possible and works without any problem:
$c = new Calc;
$k = $c;
echo $k(4,5); //outputs 20
However if I want to have another class to store an instance of that object,
this doesn't work:
class Test {
public $k;
function __construct() {
$c = new Calc;
$this->k = $c; //Just to show a similar situation than before
// $this-k = new Calc; produces the same error.
}
}
The error occurs when we try to call it like:
$t = new Test;
echo $t->k(4,5); //Error: Call to undefined method Test::k()
I know that a "solution" could be to have a function inside the class Test (named k) to "forward" the call using call_user_func_array but that is not elegant.
I need to keep that instance inside a common class (for design purposes) and be able to call it as function from other classes... any suggestion?
Update:
I found something interesting (at least for my purposes):
If we assign the "class variable" into a local variable it works:
$t = new Test;
$m = $t->k;
echo $m(4,5);
PHP thinks you want to call a method k on instance $t when you do:
$t->k(4, 5)
which is perfectly reasonable. You can use an intermediate variable to call the object:
$b = $t->k;
$b(4, 5);
See also bug #50029, which describes your issue.
When you do $test->k(), PHP thinks you are calling a method on the $test instance. Since there is no method named k(), PHP throws an exception. What you are trying to do is make PHP return the public property k and invoke that, but to do so you have to assign k to a variable first. It's a matter of dereferencing.
You could add the magic __call method to your Test class to check if there is a property with the called method name and invoke that instead though:
public function __call($method, $args) {
if(property_exists($this, $method)) {
$prop = $this->$method;
return $prop();
}
}
I leave adding the arguments to the invocation to you.
You might also want to check if the property is_callable.
But anyway, then you can do
$test->k();
You can not use method syntax (like $foo->bar() ) to call closures or objects with __invoke, since the engine always thinks this is a method call. You could simulate it through __call:
function __call($name, $params) {
if(is_callable($this->$name)) {
call_user_func_array($this->$name, $params);
}
}
but it would not work as-is.
If you call $test->k() PHP will search for a method called "k" on the $test instance and obviously it will throws an Exception.
To resolve this problem you can create a getter of the property "k"
class Test {
public $k;
function __construct() {
$c = new Calc;
$this->k = $c; //Just to show a similar situation than before
// $this-k = new Calc; produces the same error.
}
public function getK() {
return $this->k;
}
}
So now you can use the functor in this way:
$t = new Test();
echo $t->getK()(4,5);