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';
}
}
}
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.
In few worlds : I would like to do the same but in PHP.
In details : I have a Class A method that instantiates a Class X which can be a Class B or C.
Class A
class A{
...
protected function init(){
if( !empty( $this->sub_pages ) ){
foreach ( $this->sub_pages as $sub_page ){
$class = $sub_page['class_path'];
//Here, I need to check if ClassX ( = $class) constructor has arguments.
if( no arguments){
new $class();
}else{
new $class( $sub_page['data'] );
}
}
}
}
...
}
Class B
class B{
public function __construct(){ //<-- No arguments
}
}
Class C
class C extends D{
public function __construct( $data ){ //<-- With arguments
parent::__construct( $data );
}
}
Someone know the answer ?
If you want to check whether a class has a constructor and if that constructor accepts any params or not then you can do it using PHP's Reflection Class for example:
$reflector = new \ReflectionClass('SomeClass');
$constructor = $reflector->getConstructor();
if ($constructor && $constructor->getParameters()) {
// Since your class needs $sub_page['data'] and
// you already have this in your current scope
$instance = $reflector->newInstanceArgs($sub_page['data']);
} else {
$instance = new SomeClass;
}
Btw, If you have type hinted dependencies (like other class instance) then you can find out what is the dependency and can also new up that dependent class to pass as param.
Base on #The Alpha answer, I found this :
class A{
...
protected function init(){
if( !empty( $this->sub_pages ) ){
foreach ( $this->sub_pages as $sub_page ){
try {
$reflector = new \ReflectionClass( $class );
if (!$constructor = $reflector->getConstructor()) {
printf( "The Class '%s' has not got a constructor", $class );
}else {
// has a constructor
if ( $paramsArray = $constructor->getParameters() ) {
new $class( $sub_page['data'] );
}else{
new $class();
}
}
} catch ( \ReflectionException $e ) {
echo $e;
}
}
}
...
}
I am using two classes: Points and Populate_Fields. The Points class has getters for various points that look like these:
class Points {
public function get_state_points($user_id) {
return $this->calculate_state_points($user_id);
}
public function get_region_points($user_id) {
return $this->calculate_region_points($user_id);
}
...
}
And the Populate_Fields class uses these methods to populate fields:
class Populate_Fields extends Points {
private function populate_state_point_value( $field ) {
$user_id = \thermal\User_Data::get_edited_user_id();
if( ! empty($user_id) ) {
$state_points = $this->get_state_points($user_id);
$field['value'] = $state_points;
update_user_meta($user_id, 'state_point_value', $state_points);
}
return $field;
}
private function populate_region_point_value( $field ) {
$user_id = \thermal\User_Data::get_edited_user_id();
$region_points = $this->get_region_points($user_id);
update_user_meta($user_id, 'region_point_value', $region_points);
$field['value'] = $region_points;
return $field;
}
}
As you can see, currently the Populate_Fields class extends the Points to make these methods available under $this. However, I am not sure if extending is a good practice for this: it does not make much sense to me to make the Populate_Fields a child of Points only because it uses its methods.
Another thing I thought of, is to make an instance of the Points class as a property of the Populate_Fields class:
class Populate_Fields {
private $points;
public function __construct() {
$this->points = new Points();
}
private function populate_state_point_value( $field ) {
$user_id = \thermal\User_Data::get_edited_user_id();
if( ! empty($user_id) ) {
$state_points = $this->points->get_state_points($user_id);
$field['value'] = $state_points;
update_user_meta($user_id, 'state_point_value', $state_points);
}
return $field;
}
...
}
Is it a better practice? Or, if I am using these methods more than in these two classes, does it make sense to make them static instead and use like this:
class Points {
public static function get_state_points($user_id) {
return self::calculate_state_points($user_id);
}
...
}
class Populate_Fields {
private function populate_state_point_value( $field ) {
$user_id = \thermal\User_Data::get_edited_user_id();
if( ! empty($user_id) ) {
$state_points = Points::get_state_points($user_id);
$field['value'] = $state_points;
update_user_meta($user_id, 'state_point_value', $state_points);
}
return $field;
}
...
}
Use "dependency injection" to make a Points instance required when instantiating Populate_Fields:
class Populate_Fields {
private $points;
public function __construct(Points $pointsObj) {
$this->points = $pointsObj;
}
private function populate_state_point_value( $field ) {
$user_id = \thermal\User_Data::get_edited_user_id();
if( ! empty($user_id) ) {
$state_points = $this->points->get_state_points($user_id);
$field['value'] = $state_points;
update_user_meta($user_id, 'state_point_value', $state_points);
}
return $field;
}
...
}
http://php-di.org/doc/understanding-di.html
I am writing a function that calls another class and returns it so that I can execute my methods as $this->myUser()->getUsername()... now my question is:
Is there a way for me to intercept the ->getUsername() from inside myUser() or intercept the fail to find?...
This is because I want to use another class that is myUserExtended() and I was wondering if I could route them both together inside myUser(). so that if ->getCity() is not in myUser() it automaticaly goes on and uses another variable that containts myUserExtended().
Probably is not possible, but it is worth asking.
private function myUser( $setMyUser = false ) {
if( $setMyUser ) {
$this->_myUser = $setMyUser;
}
if( empty( $this->$_myUser ) ) {
$this->_myUser = UserQuery::create()->findPK( $this->variables('userID') );
}
return $this->_myUser;
}
Something like this may do the trick:
public function __call($name, $args)
{
if (function_exists($this->_myUser->$name())) {
return $this->_myUser->$name();
}
return $this->_myUserExtended->$name();
}
You'll obviously have to adjust it if you are using any arguments.
Maybe something like this will help:
class MyUserClass {
private $_myUser;
public function myUser( $setMyUser = false ) {
if( $setMyUser ) {
$this->_myUser = $setMyUser;
}
if( empty( $this->$_myUser ) ) {
$this->_myUser = UserQuery::create()->findPK( $this->variables('userID') );
}
return $this; //if you want to use method chaining, you have to return $this
}
public function __call($name, $arguments) {
$ext = new myUserExtended();
$ext->$name($arguments);
return $ext;
}
}
$myUser = new MyUserClass();
$myUser->myUser()->getCity("New York"); //this will call $ext->getCity(array("New York"))
$this->myUser()->getUsername() is equivalent to $user = $this->myUser(); $user->getUsername(). If you can add methods to whatever class the $user variable contains, then you can use the __call() magic method and check if ->getCity() exists. Or you could just add a method called getCity() that calls through to myUserExtended().
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();