I wonder if this below is possible in php class object, just as I would do in javascript (jquery).
In jquery, I would do,
(function($){
var methods = {
init : function( options ) {
// I write the function here...
},
hello : function( options ) {
// I write the function here...
}
}
$.fn.myplugin = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist.' );
}
return this;
};
})(jQuery);
So, when I want to call a function inside myplugin, I just do this,
$.fn.myplugin("hello");
So, I thought, there might be a way to do this in php as well when you come to write a class?
$method = (object)array(
"init" => function() {
// I write the function here...
},
"hello" => function() {
// I write the function here...
}
);
EDIT:
Could it be a class like this?
class ClassName {
public function __construct(){
//
}
public function method_1(){
$method = (object)array(
"init" => function() {
// I write the function here...
},
"hello" => function() {
// I write the function here...
}
);
}
public function method_2(){
$method = (object)array(
"init" => function() {
// I write the function here...
},
"hello" => function() {
// I write the function here...
}
);
}
}
Your $.fn.myplugin function is very similar to the __call() magic function in PHP. However, you would have to define it in a class and emulate the logic:
class Example {
private $methods;
public function __construct() {
$methods = array();
$methods['init'] = function() {};
$methods['hello'] = function() {};
}
public function __call($name, $arguments) {
if( isset( $methods[$name])) {
call_user_func_array( $methods[$name], $arguments);
} else if( $arguments[0] instanceof Closure) {
// We were passed an anonymous function, I actually don't think this is possible, you'd have to pass it in as an argument
call_user_func_array( $methods['init'], $arguments);
} else {
throw new Exception( "Method " . $name . " does not exist");
}
}
}
Then, you would do:
$obj = new Example();
$obj->hello();
It's not tested but hopefully it's a start.
PHP supports Closure (Anonymous functions)
similar to jQuery take a look at
function x(callable $c){
$c();
}
then use
x(function(){
echo 'Hello World';
});
class ClassName {
public function __construct(){
//this is your init
}
public function hello(){
//write your function here
}
}
is how you would write it
then
$a = new ClassName()
$a->hello();
to call it
Related
How to create a callback function that has multiple callback functions from an array:
$fn = function() { echo '1';};
$fn2 = function() { echo '2';};
$array = [
$fn,
$fn2
];
$callback = ... $array; // Calls first $fn then $fn2.
Bigger context:
I am using some library where some class has a callback function as a property, this refers to a function that can be executed before the actual operation.
public function before(callable $fn)
{
$this->before = $fn;
return $this;
}
By default, for my work, I fill it with a certain function, so you can't add another one.
Due to the fact that the class has $this->before and few key methods privately created, I am not able to overwrite by my own classes and I unfortunately it is a third-party library and I can't make changes to it
I came up with the idea of overriding the class and the main method that is used to set this callback so that my class will have an array, and at the point of adding the callback function before calling the parent, I will create one callback function from all the functions added to the array.
/**
* #var callable[]
*/
private array $beforeCallbacks = [];
public function before(callable $fn): ChildrenClass
{
$this->beforeCallbacks[] = $fn;
foreach ($this->beforeCallbacks as $callback) {
if (!isset($newCallback)) {
$newCallback = $callback;
}
$newCallback .= $callback; // As you can guess, it doesn't work:C
}
return parent::before($newCallback);
}
Any suggestions?
I wonder if that's even possible.
And what if I wanted to inject a parameter into each function, is there any way to handle this?
One option is to wrap your callbacks in a structure that can handle calling multiple and in the order you want. The version below uses __invoke but you could do whatever callable syntax for PHP that you want.
class MultipleCaller {
private $callbacks = [];
public function addCallback(callable $fn) {
$this->callbacks[] = $fn;
}
public function __invoke() {
foreach($this->callbacks as $callback) {
$callback();
}
}
}
$mc = new MultipleCaller();
$mc->addCallback(static function () { echo 1, PHP_EOL; } );
$mc->addCallback(static function () { echo 2, PHP_EOL; } );
$mc();
edit
Yes, arguments can be passed. One option is to use ... to pass things through
class MultipleCaller {
private $callbacks = [];
public function addCallback(callable $fn) {
$this->callbacks[] = $fn;
}
public function __invoke(...$args) {
foreach($this->callbacks as $callback) {
$callback(...$args);
}
}
}
$mc = new MultipleCaller();
$mc->addCallback(static function (...$args) { echo 'Function 1', PHP_EOL, var_dump($args), PHP_EOL; } );
$mc->addCallback(static function (...$args) { echo 'Function 2', PHP_EOL, var_dump($args), PHP_EOL; } );
function doWork(callable $fn, ...$args) {
$fn(...$args);
}
doWork($mc, 'alpha', 'beta');
Demo: https://3v4l.org/TGdJq
func_get_args could also be used in a similar fashion
edit 2
The magic __invoke can be skipped, too, if you'd rather have a more explicit method to call. You could then use [$mc, 'invoke'] or the more modern $mc->invoke(...) syntax.
<?php
class MultipleCaller {
private $callbacks = [];
public function addCallback(callable $fn) {
$this->callbacks[] = $fn;
}
public function invoke(...$args) {
foreach($this->callbacks as $callback) {
$callback(...$args);
}
}
}
$mc = new MultipleCaller();
$mc->addCallback(static function (...$args) { echo 'Function 1', PHP_EOL, var_dump($args), PHP_EOL; } );
$mc->addCallback(static function (...$args) { echo 'Function 2', PHP_EOL, var_dump($args), PHP_EOL; } );
function doWork(callable $fn, ...$args) {
$fn(...$args);
}
doWork([$mc, 'invoke'], 'alpha', 'beta');
doWork($mc->invoke(...), 'alpha', 'beta');
Demo: https://3v4l.org/Zd1De#v8.2.2
I found a solution:
$fn = function() { var_dump('First');};
$fn2 = function() { var_dump('Second');};
$fn3 = function() { var_dump(func_get_args());};
$array = [
$fn,
$fn2,
$fn3
];
$callback = function () use ($array) {
foreach ($array as $fn) {
$fn(...func_get_args());
}
};
$callback('Passed parameter');
will display:
string(5) "First"
string(6) "Second"
array(1) {
[0]=>
string(16) "Passed parameter"
}
I need to call a function that's inside an object, inside a class. Of course, for "On The Fly" class methods I'm able to call it using using __call & __set magics but not on this case. Below is the example for this situation.
class mainclass
{
public $v1 = "Hello";
public $fn = null;
function __construct( )
{
$this->fn = (object) [ "fn1" => null,
"fn2" => null,
];
}
public function __call( $name, array $args )
{
return call_user_func_array( $this->$name, $args );
}
public function fn3()
{
echo "This of course works! <br />";
}
}
$main = new mainclass();
$main->fn4 = function()
{
echo "Even this works! <br />";
};
$main->fn->fn1 = function()
{
echo $this->v1 . " World :)";
};
$main->fn3(); // This of course works!
$main->fn4(); // Even this works!
$main->fn->fn1(); //Call to undefined method stdClass::fn1()
There is a possibility to call the function "f1" this way: $main->fn->fn1() ?
If not, any suggestion without drastic changes?
Unfortunately this is not JavaScript and don't like the way is handled this class but I have to give it a try
The only and easy workaround I have for this case is to change the object in anonymous class. During this process you have to store the scope of main class on the internal anonymous class(es) using a similar variable name, "$_this".
class mainclass
{
public $v1 = "Hello";
public $fn = null;
function __construct( )
{
$this->fn = new class( $this)
{
public $_this = null;
public function __construct( $mainscope )
{
$this->_this = &$mainscope;
}
public function __call( $method, array $args )
{
if ( isset( $this->{ $method } ) )
{
return call_user_func_array( $this->$method, $args );
}
elseif ( isset( $this->_this->{ $name } ) )
{
return call_user_func_array( $this->_this->{ $name }, $args);
}
}
public function __set( $name, $value )
{
$this->{ $name } = is_callable( $value ) ? $value->bindTo( $this, $this ) : $value;
}
};
}
public function __call( $method, array $args )
{
return call_user_func_array( $this->{ $method }, $args );
}
public function __set( $name, $value )
{
$this->{ $name } = is_callable( $value ) ? $value->bindTo( $this, $this ) : $value;
}
public function fn3()
{
echo "This of course works! <br />";
}
}
$main = new mainclass();
$main->fn4 = function()
{
echo "Even this works! <br />";
};
$main->fn->fn1 = function()
{
echo $this->_this->v1 . " World :)";
};
$main->fn3(); // This of course works!
$main->fn4(); // Even this works!
$main->fn->fn1(); //Hello World :)
It turns out not very ugly and also manageable. Anyway this is the only option for now.
($main->fn->fn1)(); should be working. However, you can't access $this in the anonymous function
$main->fn->fn1();
fn1 is an attribute try using $main->fn.
So, I have something like this:
class ClassA{
public function doCallback($callback){
call_user_func_array($callback, array());
}
public function doSomething(){
return 12345;
}
}
class ClassB{
public function runMe(){
$classA = new ClassA();
$classA->doCallback(function(){
$this->doSomething();
});
}
}
I am trying to figure out how, if possible I can use $this or something similar in a callback function that will refer to the class that the callback is running on (Not the class it is in) if that makes sense.
So in my above example I would like $this->doSomething(); where $this means ClassA and not ClassB. Currently $this is referring to ClassB. Is there something that I can use to say I want ClassA?
EDIT
Here is the actual method that I am using
public function save(){
$id = (int)$this->input->post("id");
$title = $this->input->post("title");
$uid = (int)$this->session->get("id");
$this->db->getTable("content")
->has(array("user_id" => $uid, "name" => $title), function(){
echo json_encode(array("error" => "Name already exists."));
}, function() use($id, $uid, $title){
//$this->db->getTable("content")
$this->update(array("name" => $title), array(
"user_id" => $uid,
"content_id" => $id), function($rows){
if($rows > 0){
$error = "";
}else{
$error = "Unknown error occurred";
}
echo json_encode(array("error" => $error));
});
});
}
$this->db->getTable("content") returns a Database Object, and has() is a method in the object. I was hoping that I could use a shorthand way to access $this->db->getTable("content") without having to call it again in the callback, or passing it through call_user_func_array as a parameter or without the use of use().
the method has():
https://github.com/ZingPHP/Zing/blob/master/Zing/src/Modules/Database/DBOTable.php#L311
EDIT
I think in my callback function I need to do something like this, but I don't think it is working:
public function myFunc(callback $callback){
$callback->bindTo($this, $this);
return call_user_func_array($callback, array());
}
You can go through the public interface of ClassA.
class ClassA
{
public function doCallback($callback)
{
call_user_func_array($callback, array());
}
public function doSomething()
{
echo "Doing something...\n";
}
}
class ClassB
{
private $_oSubject;
public function __construct(ClassA $oSubject)
{
$this->_oSubject = $oSubject;
}
public function runMe()
{
$this->_oSubject->doCallback(function() {
$this->_oSubject->doSomething();
});
}
}
$oA = new ClassA();
$oB = new ClassB($oA);
$oB->runMe();
I got it! I just need to add $newCallback = $callback->bindTo($this, $this); in the callback class, and then in ClassB I can use $this to refer to ClassA.
class ClassA{
public function doCallback($callback){
$callback = $callback->bindTo($this, $this);
call_user_func_array($callback, array());
}
public function doSomething(){
return 12345;
}
}
class ClassB{
public function runMe(){
$classA = new ClassA();
$classA->doCallback(function(){
$this->doSomething();
});
}
}
I was wondering how we can use the call_user_func_array() safely in the code.
Following coded function way is safe ?
function outertext() {
// …
if ($this->dom && $this->dom->callback!==null) {
call_user_func_array($this->dom->callback, array($this));
}
// …
}
What is best possible use of the call_user_func_array() of PHP. how we can use this function safely
Proof of concept: (how attacker can attack on this function)
<?php
class simple_html_dom_node {
private $dom;
public function __construct() {
$callback = array(new WP_Screen(), 'render_screen_meta');
$this->dom = (object) array('callback' => $callback);
}
}
class WP_Screen {
private $_help_tabs;
public $action;
function __construct() {
$count = array('count' => 'echo "schwag" > /tmp/1337h4x0rs');
$this->action = (object) $count;
$this->_help_tabs = array(array(
'callback' => 'wp_generate_tag_cloud',
'topic_count_scale_callback' => 'shell_exec'));
}
}
echo serialize(new simple_html_dom_node()).'𝌆';
?>
Check this modified example
<?php
class WP_Screen {
private $_help_tabs;
public $action;
function __construct() {
$count = array('count' => 'echo "schwag" > /tmp/1337h4x0rs');
$this->action = (object) $count;
$this->_help_tabs = array(array(
'callback' => 'wp_generate_tag_cloud',
'topic_count_scale_callback' => 'shell_exec'));
}
public function render_screen_meta()
{
echo __METHOD__;
}
}
class simple_html_dom_node
{
private $dom;
public function __construct()
{
$callback = array(new WP_Screen(), 'render_screen_meta');
$this->dom = (object) array('callback'=>$callback);
}
public function outer_text()
{
//verify the dom callback function here
if(is_callable($this->dom->callback))
{
//invoke the method here
call_user_func_array($this->dom->callback, array());
}
}
}
//create an object
$obj = new simple_html_dom_node();
//invoke the method
$obj->outer_text();
checkout the following example
<?php
function foobar($arg, $arg2) {
echo __FUNCTION__, " got $arg and $arg2\n";
}
class foo {
function bar($arg, $arg2) {
echo __METHOD__, " got $arg and $arg2\n";
}
}
// Call the foobar() function with 2 arguments
call_user_func_array("foobar", array("one", "two"));
// Call the $foo->bar() method with 2 arguments
$foo = new foo;
call_user_func_array(array($foo, "bar"), array("three", "four"));
?>
Output would be
foobar got one and two
foo::bar got three and four
validate first the method exist in the class or not using any of the function
method_exists or is_callable
Reference:
http://in2.php.net/manual/en/function.is-callable.php
http://in2.php.net/method_exists
Example:
<?php
class someClass {
function someMethod()
{
}
}
$anObject = new someClass();
$methodVariable = array($anObject, 'someMethod');
is_callable($methodVariable, true, $callable_name);
if($callable_name)
{
//use your function call here
call_user_func_array(callback_function, array(object));
}
I have a class 'abc', with several functions inside it:
'abc_function1'
'abc_function2'
'abc_function3'
'abc_function4'
'abc_function5'
I would like to call a function of the class 'abc' according to a parameter that I enter, a string containing 'function1' or 'function 4' for example, to refer to the corresponding function of the class.
I hope I've made myself clear ;)
Thanks a lot for your help
Not exactly sure why but this has a certain code smell in my opinion. But anyway...
Method a): Implement the "magic" method __call($name, $params).
<?php
class Foo {
public function abc_function1() {
echo "function #1";
}
public function abc_function2() {
echo "function #2";
}
public function abc_function3() {
echo "function #3";
}
public function __call($name, $params) {
$fqn = 'abc_'.$name;
if ( method_exists($this, $fqn) ) {
call_user_func_array( array($this, $fqn), $params);
}
}
}
$f = new Foo;
$f->function2();
Method b): Same idea, just without the automagical mapping.
<?php
class Foo {
public function abc_function1() {
echo "function #1";
}
public function abc_function2() {
echo "function #2";
}
public function abc_function3() {
echo "function #3";
}
public function doSomething($x, $y, $z) {
$fqn = 'abc_'.$x;
if ( method_exists($this, $fqn) ) {
call_user_func_array( array($this, $fqn), array($y, $z));
}
}
}
$f = new Foo;
$f->doSomething('function2', 1, 2);
Method c) If you know the number of parameter you can also use
$this->$fqn($,y, $z)
instead of
call_user_func_array( (array($this, $fqn), array($y, $z) );
see also: http://docs.php.net/call_user_func_array and http://docs.php.net/functions.variable-functions
$class_instance = new class();
call_user_func(
array( $class_instance, $your_string_containing_the_fx_name ),
$the_parameters_you_want_to_pass
);
You can use the variable functions feature of PHP:
function call_function( $string ) {
$var = 'abc_' . $string;
$retval = $var(); // this will call function named 'abc_'
// plus the contents of $string
return $retval;
}