I have a class A which needs a new method if one of its arguments is set up to true.
Class A
Class A{
private $is_new_method;
public function __construct( $is_new_method = false, $new_method_name = "" ){
$this->is_new_method = $is_new_method;
if( $this->is_new_method ){
//TODO : add new method in this class based on $new_method_name
}
}
}
I saw runkit_method_add but it needs (PECL runkit >= 0.7.0).
Notice: this function will be call inside a core of a framework like this :
$foo = new A();
$foo->myNewFunction();
So what is the best way to do this ?
$a = new Inflector([
'foo' => function($val){echo sprintf('%s calls foo()', $val[0] ?? 'none!').PHP_EOL;},
'bar' => function($val){echo sprintf('%s calls bar()', $val[0] ?? 'none!').PHP_EOL;},
'baz' => function($val){echo sprintf('%s calls baz()', $val[0] ?? 'none!').PHP_EOL;},
]);
$a->foo('Ahnold');
$a->bar('Elvis');
$a->baz('Lerhman');
$a->theBreaks('Who');
class Inflector
{
private $methods = [];
public function __construct(array $methods)
{
$this->methods = $methods;
//var_dump($this->methods);
}
public function __call(string $methodName, $params)
{
if (isset($this->methods[$methodName])) {
$this->methods[$methodName]($params);
}
throw new InflectionDeceptionException();
}
}
class InflectionDeceptionException extends \Exception
{
public function __construct($message = "Say what?")
{
return parent::__construct($message);
}
}
https://3v4l.org/kLdOp
Gives:
Ahnold calls foo()
Elvis calls bar()
Lerhman calls baz()
Thanks #Jared Farrish !
I found my solution based on your recommandation.
Class A{
private $is_new_method;
private $new_method;
public function __construct( $is_new_method = false, $new_method_name = "" ){
$this->is_new_method = $is_new_method;
if( $this->is_new_method ){
$new_method = $this->make_my_new_method( $new_method_name );
}
}
private function make_my_new_method( $method_name ){
$functionName = "foo_" . $method_name;
$$functionName = function ( $item ) {
print_r( $item );
}
return $$functionName;
}
public function __call( $method, $args){
if ( isset( $this->new_method ) ) {
$func = $this->new_method;
return call_user_func_array( $func, $args );
}
}
}
Related
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.
I have a class with a couple of static functions. One of my functions build a variable and I want to use that variable in another static function.
How can I call that variable?
class MyClass{
public static function show_preprice_value_column( $column, $post_id ) {
if ( $column == 'product_preprice' ) {
$product_preprice = get_post_meta( $post_id, 'product_preprice', true );
if ( intval( $product_preprice ) > 0 ) {
echo $product_preprice;
}
}
}
public static function show_off_value_column( $column, $post_id ) {
if ( $column == 'product_off' ) {
var_dump((int)self::show_preprice_value_column());
}
}
}
Do you mean this?
<?php
class MyClass
{
private static $var;
public static function funcA()
{
self::$var = "a";
}
public static function funcB()
{
self::$var = "b";
}
}
I used this code :
class Test {
public static function test1(){
return 12;
}
public static function test2(){
$var = self::test1();
echo $var;
echo "\n".gettype($var);
}
}
Test::test2();
And get this as result:
12
integer
So you need to use return after your echo to communicate the value
I would like to know if it is possible to call a function of a parent file inside a included file and how that could be work.
For an example we got that:
parent_file.php :
<?php
if ( ! class_exists( 'Parent_Class' ) ) {
class Parent_Class {
public $id = 10;
public static function getInstance() {
if ( ! ( self::$_instance instanceof self ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
public function init() {
include 'child-file.php';
$child = new Child_Class($id);
$child->action();
}
public function edit($values_of_id) {
return $values_of_id;
}
?>
child_file.php :
<?php
if ( ! class_exists( 'Child_Class' ) ) {
class Child_Class {
private $id;
function __construct(){
$params = func_get_args();
if(!empty($params))
foreach($params[0] as $key => $param)
if(property_exists($this, $key))
$this->{$key} = $param;
parent::__construct( array(
'id' => $this->id,
) );
}
public function action() {
$url = 'http://myserver.com/edit_child.php?page='. $_REQUEST['page'] .'&action=select&id='. absint($this->id) ) );
$action = '<a href='. $url .'>Edit</a>'
return $action;
}
public function select_table_row() {
if ( isset( $_GET['action'] ) && !empty( $_GET['action'] ) )
$row = $_GET['id'];
$connection = new mysqli($servername, $username, $password, $dbname); // fictitious params
$query = "SELECT * FROM MyTable WHERE id = $row";
$values_of_id = mysqli_query($connection, $query);
// Call function of parent_file.php
edit($values_of_id);
}
$this->select_table_row();
?>
This is a fictitious example and I know that the code couldn't work like this. I just want to aim to my question and make my thoughts visual and maybe more comprehensible.
Important is that I cannot include parent_file.php in my child_file.php because the Child_Class could be access from multiple files.
I'm sorry if this question was already asked. I'm limited in my buzzwords for this topic and couldn't find anything like this.
You have to pass the parent class object to the child class, something like this:
class parentClass {
private $str;
public function __construct($str){
$this->str = $str;
}
public function getChild() {
$obj = new childClass($this);
$obj->callParent("send");
}
public function send() {
echo $this->str;
}
}
class childClass {
private $parent;
public function __construct($parent) {
$this->parent = $parent;
}
public function callParent($method) {
return $this->parent->$method();
}
}
$obj = new parentClass("hello");
$obj->getChild(); // prints "hello"
Demo: https://eval.in/403427
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 am wondering if there is a way to attach a new method to a class at runtime, in php.
I mean, not on an instance level but directly to the class, so that all newly created instances, have this new method.
Can such a thing be done with reflection?
Thanks
Yes, you can.
Below is the way to create method in runtime in php 5.4.x.
The anonymous function is represented by Closure class started from 5.3.x. From 5.4.x, it add a Closure::bind static method to bind the anonymous function to a particular object or class.
Example:
class Foo {
private $methods = array();
public function addBar() {
$barFunc = function () {
var_dump($this->methods);
};
$this->methods['bar'] = \Closure::bind($barFunc, $this, get_class());
}
function __call($method, $args) {
if(is_callable($this->methods[$method]))
{
return call_user_func_array($this->methods[$method], $args);
}
}
}
$foo = new Foo;
$foo->addBar();
$foo->bar();
Did some playing around with whole thing. Seems that only thing you can potentially do with ReflectionClass is to replace an existing method. But even that would be indirectly.
I actually do not know any class-based language, where dynamic classes exist (then again, my knowledge is quite limited). I have seen it done only in prototype-based languages (javascript, ruby, smalltalk). Instead what you can do, in PHP 5.4, is to use Closure and add new methods to an existing object.
Here is a class which would let you perform such perversion to any object:
class Container
{
protected $target;
protected $className;
protected $methods = [];
public function __construct( $target )
{
$this->target = $target;
}
public function attach( $name, $method )
{
if ( !$this->className )
{
$this->className = get_class( $this->target );
}
$binded = Closure::bind( $method, $this->target, $this->className );
$this->methods[$name] = $binded;
}
public function __call( $name, $arguments )
{
if ( array_key_exists( $name, $this->methods ) )
{
return call_user_func_array( $this->methods[$name] , $arguments );
}
if ( method_exists( $this->target, $name ) )
{
return call_user_func_array(
array( $this->target, $name ),
$arguments
);
}
}
}
To use this, you have to provide constructor with an existing object. Here is small example of usage:
class Foo
{
private $bar = 'payload';
};
$foobar = new Foo;
// you initial object
$instance = new Container( $foobar );
$func = function ( $param )
{
return 'Get ' . $this->bar . ' and ' . $param;
};
$instance->attach('test', $func);
// setting up the whole thing
echo $instance->test('lorem ipsum');
// 'Get payload and lorem ipsum'
Not exactly what you want, but AFAIK this is as close you can get.
Have you taken a look at create_function() in the docs? You might also achieve the desired result by overloading.
This is possible with the runkit extension's runkit_method_add(). Be careful using this in production though.
Example:
<?php
class Example {}
$e = new Example();
runkit_method_add(
'Example',
'add',
'$num1, $num2',
'return $num1 + $num2;',
RUNKIT_ACC_PUBLIC
);
echo $e->add(12, 4);
You can use one of the below two methods also.
function method1()
{
echo "In method one.";
}
function method2()
{
echo "In method two.";
}
class DynamicClass
{
function __construct(){
$function_names = ['method1'];
foreach ($function_names as $function_name) {
if (function_exists($function_name)) {
$this->addMethod($function_name);
}
}
}
function addMethod($name)
{
$this->{$name} = Closure::fromCallable($name);
}
public function __call($name, $arguments)
{
return call_user_func($this->{$name}, $arguments);
}
}
$obj = new DynamicClass();
//Call method1 added in constructor
$obj->method1();
//Add method
$obj->addMethod('method2');
$obj->method2();