Call a method inside an object that's inside a php class - php

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.

Related

How to add a method inside the constructor class in PHP?

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 );
}
}
}

Call parent function inside included/required file

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

php create class method at runtime

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();

php get visibility

Is it possible to get the visibility of methods and properties inside a class in php?
I want to be able to do something like this:
function __call($method, $args)
{
if(is_callable(array($this,$method))
{
if(get_visibility(array($this,$method)) == 'private')
//dosomething
elseif(get_visibility(array($this,$method)) == 'protected')
//dosomething
else
//dosomething
}
}
is_callable takes visibility into account, but since you are using it from inside the class it will always evaluate to TRUE.
To get the method visiblity, you have to use the Reflection API and check the method's modifiers
Abridged example from PHP Manual:
class Testing
{
final public static function foo()
{
return;
}
}
// this would go into your __call method
$foo = new ReflectionMethod('Testing', 'foo');
echo implode(
Reflection::getModifierNames(
$foo->getModifiers()
)
); // outputs finalpublicstatic
The same is available for properties.
However, due to the complexity of reflecting on a class, this can be slow. You should benchmark it to see if it impacts your application too much.
You might want to consider using PHP's Reflection API for this. However, I should also ask you why you want to do this, because Reflection usually only gets used in situations that are a bit hacky to begin with. It is possible though, so here goes:
<?php
class Foo {
/**
*
* #var ReflectionClass
*/
protected $reflection;
protected function bar( ) {
}
private function baz( ) {
}
public function __call( $method, $args ) {
if( ( $reflMethod = $this->method( $method ) ) !== false ) {
if( $reflMethod->isPrivate( ) ) {
echo "That's private.<br />\n";
}
elseif( $reflMethod->isProtected( ) ) {
echo "That's protected.<br />\n";
}
}
}
protected function method( $name ) {
if( !isset( $this->methods[$name] ) ) {
if( $this->reflect( )->hasMethod( $name ) ) {
$this->methods[$name] = $this->reflect( )->getMethod( $name );
}
else {
$this->methods[$name] = false;
}
}
return $this->methods[$name];
}
protected function reflect( ) {
if( !isset( $this->reflection ) ) {
$this->reflection = new ReflectionClass( $this );
}
return $this->reflection;
}
}
$foo = new Foo( );
$foo->baz( );
$foo->bar( );
This answer is a bit late, but I feel there is still some added value by mentioning get_class_methods() in combination with method_exists():
<?php
class Foo {
// ...
public function getVisibility($method) {
if ( method_exists($this, $method) && in_array($method, get_class_methods($this)) ) {
return 'protected or public';
} else {
return 'private';
}
}
}

wordpress-like model to view api event system (MVC)

Wordpress has a nice api system. I'm struggling in creating an equal flexible one using a more traditional MVC interpretation. A typical view class could be like this:
class View
{
public function set($name, $value)
{
$this->data[$name] = $value
}
public function __get($name) ... you know how it works
}
A use case would be a controller adding an order contain order lines:
$view->add('order', $orderModel);
Then the template file could have something like
foreach ($this->order->getOrderLines() as $orderLine)
{
?><div><?php echo $orderLine->getItemName(); ?><div><?php
}
At least this is what is commonly seen in many PHP MVC frameworks. I'm open to alternative implementations that solve the question:
How to add an event system like wordpress has? Eg a filter OrderLineItemName.
Okay,
I am not 100% exactly what you want to do, but I have an idea.
Maybe you mean something like this:
class View {
public $hooks = array('getsomeVar');
public $hooks_functions = array();
public $attributes = array();
public function __set($k,$v) {
$this->attributes[$k] = $v;
}
public function __get($k) {
if (isset($this->attributes[$k])){
$hooks = $this->get_functions_by_hook('get' . $k);
if (!empty($hooks)){
foreach ($hooks as $klass=>$methods) {
if (class_exists($klass)){
$class = new $klass();
foreach ($methods as $method) {
if (method_exists($class,$method)){
$this->attributes[$k] = $class->$method($this->attributes[$k]);
}
}
}
}
}
return $this->attributes[$k];
} else {
throw new Exception($k . " is not a view variable");
}
}
public function register_filter($name,$class,$method) {
if (in_array($name,$this->hooks)){
$this->hooks_functions[$name][$class][] = $method;
} else {
throw new Exception($name . ' is not a valid hook');
}
}
public function get_functions_by_hook($name) {
if (array_key_exists($name,$this->hooks_functions)){
return $this->hooks_functions[$name];
}
return array();
}
}
class MyPlugin {
public function fix_string($str) {
return str_replace("ruby",'php',$str);
}
}
$v = new View();
$v->someVar = 'ruby is great';
$v->register_filter('getsomeVar','MyPlugin','fix_string');
echo $v->someVar;
or you can use this method, which is more event like. Again sample code, but you should be able to cannabalize it.
class EventDispatcher {
public static $listeners = array();
public static function registerListener(&$instance) {
if (!in_array($isntance,self::$listeners)){
array_push(self::$listeners,$instance);
}
}
public static function dispatchEvent($name,&$value) {
foreach (self::$listeners as $listener) {
if (method_exists($listener,'interests')){
$funcs = $listener->interests();
if (array_key_exists($name,$funcs)){
foreach ($funcs as $f) {
$value = $listener->$f($value);
}
}
}
}
}
}
class Plugin {
public static function registerPlugin($class_name) {
if (class_exists($class_name)){
EventDispatcher::registerListener(new $class_name());
}
}
}
class Model {
public function __construct() {
EventDispatcher::registerListener($this);
}
public function special($value) {
echo "I got called too!\n\n";
return $value;
}
public function interests() {
return array(
"getsomeVar" => "special",
);
}
}
class View {
public $attributes = array();
public function __set($k,$v) {
$this->attributes[$k] = $v;
}
public function __get($k) {
if (isset($this->attributes[$k])){
EventDispatcher::dispatchEvent('get' . $k,$this->attributes[$k]);
return $this->attributes[$k];
} else {
throw new Exception($k . " is not a view variable");
}
}
}
class MyPlugin {
public function fix_string($str) {
return str_replace("ruby",'php',$str);
}
public function interests() {
return array(
"getsomeVar" => "fix_string",
);
}
}
Plugin::registerPlugin('MyPlugin');
$model = new Model();
$v = new View();
$v->someVar = 'ruby is great';
echo $v->someVar;
This is just some sample code, I don't do it this way at all, but it seems like it may be what you are talking about.
Cheers,
Jason
Addendum:
Most of this stuff is about the WP codebase.
WP accesses variables set in the global scope that are modified like so:
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
global $wp_filter, $merged_filters;
$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
unset( $merged_filters[ $tag ] );
return true;
}
It has some other functions like, has_filter etc...
function apply_filters($tag, $value) {
global $wp_filter, $merged_filters, $wp_current_filter;
$args = array();
$wp_current_filter[] = $tag;
// Do 'all' actions first
if ( isset($wp_filter['all']) ) {
$args = func_get_args();
_wp_call_all_hook($args);
}
if ( !isset($wp_filter[$tag]) ) {
array_pop($wp_current_filter);
return $value;
}
// Sort
if ( !isset( $merged_filters[ $tag ] ) ) {
ksort($wp_filter[$tag]);
$merged_filters[ $tag ] = true;
}
reset( $wp_filter[ $tag ] );
if ( empty($args) )
$args = func_get_args();
do {
foreach( (array) current($wp_filter[$tag]) as $the_ )
if ( !is_null($the_['function']) ){
$args[1] = $value;
$value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
}
} while ( next($wp_filter[$tag]) !== false );
array_pop( $wp_current_filter );
return $value;
}
This is not an event driven system, it's a method lookup table which is just a giant hash that looks up the user defined functions to call.
apply_filters, and all plugin like functions are called procedurally as the code is rendered, here is an example
if ( $prefixed ) {
$value = apply_filters("pre_$field", $value);
$value = apply_filters("${field_no_prefix}_save_pre", $value);
} else {
$value = apply_filters("pre_post_$field", $value);
$value = apply_filters("${field}_pre", $value);
}
Or for actions, in an actual template view like so:
<p class="submit"><input type="submit" class="button" name="submit" value="<?php esc_attr_e('Add Category'); ?>" /></p>
<?php do_action('edit_link_category_form', $category); ?>
</form>

Categories