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().
Related
I use __remap() function to avoid any undefine method and make it redirect to index() function.
function __remap($method)
{
$array = {"method1","method2"};
in_array($method,$array) ? $this->$method() : $this->index();
}
That function will check if other than method1 and method2.. it will redirect to index function.
Now, how I can automatically grab all public function methods in that controller instead of manually put on $array variable?
You need to test if method exists and is public. So you need use reflection and method exists. Something like this:
function __remap($method)
{
if(method_exists($this, $method)){
$reflection = new ReflectionMethod($this, $method);
if($reflection->isPublic()){
return $this->{$method}();
}
}
return $this->index();
}
Or you can use get_class_methods() for create your array of methods
OK, I was bored:
$r = new ReflectionClass(__CLASS__);
$methods = array_map(function($v) {
return $v->name;
},
$r->getMethods(ReflectionMethod::IS_PUBLIC));
I've modified the codes and become like this.
function _remap($method)
{
$controllers = new ReflectionClass(__CLASS__);
$obj_method_existed = array_map(function($method_existed)
{
return $method_existed;
},
$controllers->getMethods(ReflectionMethod::IS_PUBLIC));
$arr_method = array();
//The following FOREACH I think was not good practice.
foreach($obj_method_existed as $method_existed):
$arr_method[] = $method_existed->name;
endforeach;
in_array($method, $arr_method) ? $this->$method() : $this->index();
}
Any enhancement instead of using foreach?
I have a class, more specific a repository. This repository will hold my validators so I can reach them whenever I want. Currently it looks like this:
class ValidatorRepository {
private $validators;
public function __construct() {
$this->validators = array();
}
public function get($key) {
return $this->validators[$key];
}
public function add($key, iValidator $value) {
$this->validators[$key] = $value;
}
public static function getInstance() {
//(...)
}
}
And with this class I would like to do something like this:
$vr = ValidatorRepository::getInstance();
$vr->add("string", new StringValidator());
I can insert something else than a instantiated object if that is for the better.
.. and later on, somewhere else;
$vr = ValidatorRepository::getInstance();
$vr->get("string"); // should return a *new* instance of StringValidator.
The idea is that the ValidatorRepository should NOT know about the classes before these are added.This works fine, as long as I return the current object.
But instead I would like a new object of the class. I could to this by putting a static getInstance() function in each validator, or use eval in some way, but I hope there might be another, less ugly, way.
I believe you should be able to do something this simple:
public function add( $key, iValidator $value ) {
$this->validators[ $key ] = get_class( $value ); // this call can be moved to get() if you wish
}
public function get( $key ) {
return new $this->validators[ $key ];
}
get_class() takes namespaces into account, so if you use namespaces then it will still be fine.
A slightly more flexible approach might be this:
public function add( $key, iValidator $value ) {
$this->validators[ $key ] = $value;
}
public function get( $key, $new = true ) {
if ($new) {
$class = get_class( $this->validators[ $key ] );
$class = new $class;
} else {
$class = $this->validators[ $key ];
}
return $class;
}
What you should be using is instead either inheritance:
abstract class Validated {
public function validate(){
foreach(self::VALIDATIONS as $val) {
// ...
}
}
}
class Person extends Validated {
protected $name;
const VALIDATIONS = array(
'name' => array( 'length' => new LengthValidator(15) )
);
}
or traits:
trait Validated {
function validate(){
// ...
}
}
class Person {
use Validated;
}
Shoving all the validation logic into a single class violates the single responsibly principle since it becomes responsible for for validating all classes which use it. It will quickly get out of hand.
Note that I have used a constant for the validations - you rarely need to change validation rules for a class during 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();
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';
}
}
}
How would I go about ensuring that the overridden parent method exists before I call it?
I've tried this:
public function func() {
if (function_exists('parent::func')) {
return parent::func();
}
}
However the function_exists never evaluates to true.
public function func()
{
if (is_callable('parent::func')) {
parent::func();
}
}
I use this for calling parent constructor if exists, works fine.
I also use the following as a generic version:
public static function callParentMethod(
$object,
$class,
$methodName,
array $args = []
) {
$parentClass = get_parent_class($class);
while ($parentClass) {
if (method_exists($parentClass, $methodName)) {
$parentMethod = new \ReflectionMethod($parentClass, $methodName);
return $parentMethod->invokeArgs($object, $args);
}
$parentClass = get_parent_class($parentClass);
}
}
use it like this:
callParentMethod($this, __CLASS__, __FUNCTION__, func_get_args());
The way to do that, is:
if (method_exists(get_parent_class($this), 'func')) {
// method exist
} else {
// doesn't
}
http://php.net/manual/en/function.method-exists.php
http://php.net/manual/en/function.get-parent-class.php
<?php
class super {
public function m() {}
}
class sub extends super {
public function m() {
$rc = new ReflectionClass(__CLASS__);
$namepc = $rc->getParentClass()->name;
return method_exists($namepc, __FUNCTION__);
}
}
$s = new sub;
var_dump($s->m());
gives bool(true). Not sure if this would work if the method was defined in a superclass of super, but it would be a matter of introducing a simple loop.