How can I use PHP reflection to set a static property? - php

I am using PHPUnit to make a mock class for testing.
class Item extends Object {
protected static $_cache;
}
I am pretty certain mocking does something like this ( please correct me if I'm wrong ):
class Mock_Item_randomstring extends Item {
}
When my Item's cache gets populated, it's checking to see that the object being passed in is an instance of Item. Since the mock is not explicitly defining $_cache, it fails the check for instance type.
PHP doesn't really document the reflection functions well at all. Is there a way to set the static variable after the fact so the class would become
class Mock_Item_randomstring extends Item {
protected static $_cache;
}
EDIT
I played around with reflection methods and ran into a variety of issues. Here is one that I'm confused about:
$mock = $this->getMock( 'Item', array( '_func' ), array(
$argument1, $argument2
));
$mock = new ReflectionClass($mock);
$mock->staticExpects( $this->exactly(2) )->method( '_func' );
I was under the assumption reflections copy the entire class. I get this error:
Call to undefined method ReflectionClass::staticExpects()

I tend to use a somewhat nasty trick for per-class static variables in subclasses:
class A {
protected static $cache;
public static function getCache() {
return static::$cache;
}
public static function setCache($val) {
static::$cache = & $val; // note the '&'
}
}
class B extends A {}
A::setCache('A');
B::setCache('B');
A::getCache(); // 'A'
B::getCache(); // 'B'
Of course, it is the best to avoid static variables in the first place. Use a dedicated cache object and inject it when the class is instantiated.

You don't have to. \Closure::bind lets you read and assign private and protected static properties. See the example code on http://www.php.net/manual/en/closure.bind.php

Related

Understanding how calling a protected method belonging to one object works under a different object

Can anyone explain why the below code works, at least for the protected method call:
<?php
class Component
{
public function callMethod($obj, $method)
{
return $obj->$method();
}
}
class Component1 extends Component
{
public function aPublicMethod()
{
return 'called aPublicMethod()';
}
protected function aProtectedMethod()
{
return 'called aProtectedMethod()';
}
private function aPrivateMethod()
{
return 'called aPrivateMethod()';
}
}
class Component2 extends Component
{}
$component1 = new Component1();
$component2 = new Component2();
echo $component2->callMethod($component1, 'aPublicMethod'); // works
echo $component2->callMethod($component1, 'aProtectedMethod'); // works
echo $component2->callMethod($component1, 'aPrivateMethod'); // returns an error because the private method is not accessible
Is it simply because both objects extend from the same parent class, and this contextual information allows Component2 to access Component1's protected methods (and properties)? This was just something I came across with a framework that follows a similar style and was trying to figure out why the second object was still able to access a protected method from another object.
Very good question.
As far as I can tell, its because $component1->aProtectedMethod(); is being called from within a scope that sits within the the inheritance chain (Component1 is a Component polymorphically, where the method is invoked). Which satisfies the requirements for accessing protected scope.
Having access to public scope speaks for itself. So this works anyway.
The error raised by calling $component1->aPrivateMethod(); from outside the class that declares it (outside private scope), also behaves as expected. Only Component1 instances can call aPrivateMethod from withing their encapsulated runtime.

Determine the name of the calling class (parent or child) in parent class method

Looking for a clean way to determine the class (in this case, either parent or child class) of the method that calls a method in the parent class.
I thought late static binding could handle this, but seems like that only really works for calling a static method directly, and not from within an instantiated object's method.
Consider the following:
abstract class ParentClass {
public function parentMethod() {
self::_log("parent.non.static");
}
public static function parentStatic() {
self::_log("parent.static");
}
public static function getClassName() {
return __CLASS__;
}
protected static function _log($key) {
$prefix = 'graphite.key.prefix';
$class = static::getClassName(); // gets the object's class, not calling class
$g_key = "{$prefix}.{$class}.{$key}";
echo "{$g_key} \n";
// Graphite::increment($g_key);
}
}
class ChildClass extends ParentClass {
public function childMethod() {
self::_log("child.non.static");
}
public static function childStatic() {
self::_log("child.static");
}
public static function getClassName() {
return __CLASS__;
}
}
$obj = new ChildClass;
$obj->childMethod(); // graphite.key.prefix.ChildClass.child.non.static
$obj->parentMethod(); // graphite.key.prefix.ChildClass.parent.non.static
ParentClass::parentStatic(); // graphite.key.prefix.ParentClass.parent.static
ChildClass::childStatic(); // graphite.key.prefix.ChildClass.child.static
Looking for a clean way to get the class that calls the _log() method without having to pass it in as a parameter. Doesn't have to be static at all, but I was playing around with the late static binding, because I thought that would work, but it just gets the name of the instantiated object, not the child/parent class of the method that calls the _log() method :-/
Edit:
Just to be clear, I'm after getting the class name of the method that called _log() from within the instantiated object (like parentMethod() and childMethod()) Don't care if _log() is static or not. If that makes it easier, fine. But the static ParentClass::parentStatic() and ChildClass::childStatic() were just to show late static bindings and what I figured might work, but not from calling within an instantiated object
http://php.net/manual/en/function.get-called-class.php
class One {
public static function test() {
echo get_called_class() . PHP_EOL;
}
}
class Two extends One {}
One::test();
Two::test();
Output:
One
Two
Also, according to the top comment in the docs static::class also works as of PHP 5.5.
get_class will get the class name of a class instance. This can also be called on $this within a class. If you have a class that extends/implements another, $this will refer the the instantiated class, meaning the child class.
Another option is to use debug_backtrace to get the stack of functions that lead up to where you currently are. You can parse the returned array to get whatever you need including line numbers, classes, functions, methods, whatever.

Save object clone as protected property

I intend to create a clone of an object's parent within the constructor of that parent. In short:
class ParentClass {
protected $property;
public function __construct() {
$this->property = clone $this;
}
}
class ChildClass extends ParentClass {
}
This works all fine, yet the problem with this code is the protected property getting populated with an instance of the ChildClass, in case the ChildClass is instantiated. I would however like it to be an instance of the ParentClass regardless of the class $this refers to.
I could of course combine debug_backtrace and new self() (in order to avoid endless recursion of constructor invocations) and assign the resulting ParentClass instance to the property, though such a soluation is verbose, as debug backtrace only returns string names of caller classes and methods.
Lastly, I could combine new self() and the provision of an argument to the instantiation of the object indicating if a "new self" should be created, but I dislike the solution because of its ugliness and redundancy.
Is there a way in PHP to find a "clone of self"?
As discussed in the comments, I think the reason this pattern is not working for you is that you have a poorly designed object hierarchy. In the example, ChildClass is a "type of" ParentClass, but also internally references a copy of ParentClass to do some delegated work.
From the comments, what you have must look something like this:
class BasicLogger {
protected $delegated_logger;
public function __construct() {
// initialise $this->delegated_logger somehow
}
public function logMessage($message, $flags) {
{
$prepared_message = $this->prepareMessage($message, $flags);
$this->deliverMessage($prepared_message);
}
private function prepareMessage($message, $flags) {
// implementation here
}
protected function deliverMessage($prepared_message) {
// implementation here
}
}
class MailLogger extends BasicLogger {
protected function deliverMessage($prepared_message) {
// different implementation here
if ( $mail_sending_failed ) {
$this->delegated_logger->logMessage('Oops, MailLogger failed...');
}
}
}
However, BasicLogger is actually performing multiple roles in the object hierarchy:
defining the interface that all loggers should adhere to (here represented as a single logMessage method)
providing a shared implementation of prepareMessage that all loggers will use, and an implementation of logMessage that depends on it plus a deliverMessage function
providing a specific implementation of deliverMessage that will be completely over-written by child classes
providing a mechanism for complex implementations to delegate to simpler implementations, without a way of distinguishing between the two
The first three roles should be separated into an interface, an abstract base class, and a simple implementation:
interface Logger {
public function logMessage($message, $flags = null);
}
abstract class BaseLogger implements Logger {
public function logMessage($message, $flags = null) {
{
$prepared_message = $this->prepareMessage($message, $flags);
$this->deliverMessage($prepared_message);
}
private function prepareMessage($message, $flags) {
// implementation here
}
abstract protected function deliverMessage($prepared_message);
}
class BasicTextLogger extends BaseLogger {
protected function deliverMessage($prepared_message) {
// implementation here
}
}
You can then use instances of BasicTextLogger wherever you need, including in other implementations of BaseLogger.
You might want to put the logic of having a delegated logger (the 4th role of my BasicLogger above) into another class for reuse. BasicTextLogger shouldn't inherit this behaviour, or you'll end up needing to provide a logger to a logger to a logger to a logger, ad infinitum.
abstract class ComplexLogger extends BaseLogger {
protected $delegated_logger;
public function __construct( Logger $delegated_logger ) {
if ( $delegated_logger instanceOf ComplexLogger ) {
throw new Exception('Attempted to delegate one complex logger to another; danger of recursion, so disallowed.');
} else {
$this->delegated_logger = $delegated_logger;
}
}
}
class MailLogger extends ComplexLogger {
protected function deliverMessage($prepared_message) {
// different implementation here
if ( $mail_sending_failed ) {
$this->delegated_logger->logMessage('Oops, MailLogger failed...');
}
}
}
This then allows you to perform Dependency Injection to provide your complex logger with a simple logger to delegate to:
$my_logger = new MailLogger( new BasicTextLogger() );
$my_logger->logMessage('Hello World!');
This may seem like a lot of different classes and interfaces, but each now has a clear responsibility. You could put the whole $delegated_logger logic into MailLogger, but you'd have to copy and paste it if you had another complex logger later. You might also be able to ignore the Logger interface, and just type-hint for classes deriving from the BaseLogger class, but it's possible you'll want an implementation that doesn't use prepareMessage at all - for instance, a DoNothingLogger.

How to assign an object instance to a Behavior?

I'm struggling to get my Behavior class to use an object instance in the callbacks.
class SomethingBehavior extends ModelBehavior
{
public function setObject($obj)
{
// do stuff
}
public function afterFind(Model $model,$results,$primary)
{
// use the $obj reference set above
}
}
Now I need the Model class to call setObject(..) before any find operations are performed. So ideally I would just assign the object I need in the constructor.
class Document extends AppModel
{
//.....
public function __construct($id,$table,$ids)
{
parent::__construct($id,$table,$ds);
$this->Something->setObject(new MyClass());
}
}
My problem is that the Behavior object isn't yet configured, and I get a not an object error when trying to use it.
I can't find any callback method for Models like in Components. For example, there is no setup or initialize method.
How can I assign the object I need to the Behavior?
You don't seem to have worked with behaviors much. Try to use the containable, tree or other core or plugin behaviors, then you will soon figure out the basics.
First of all, behaviors are attached to models (and since 2.3: loaded), not the other way around. A model then gets "richer" in functionality.
Either statically be using public $actsAs or dynamically using
$this->Behaviors->attach('Something'); // since 2.3: load() instead of attach()
It can directly access the behavior methods. Lets say we have a method foo() in your behavior.
You can then call it from your model as
$this->foo($foo, $bar);
Or from your controller as
$this->Document->Behaviors->attach('Something')
$this->Document->foo($foo, $bar);
Awesome, right?
The behavior method usually has this declaration:
public function foo(Model $Model, $foo, $bar) {
$alias = $Model->alias;
// do sth
}
As you can see, you always pass the model into it implicitly (as first argument automatically passed).
You can access all its attributes.
And do not touch the constructor of the model. no need to do that.
If you really need to pass an object in at runtime, why does your approach not work?
public function setObject(MyClass $obj) {
$this->Obj = $obj;
}
Now you can internally use the object from your behavior methods
public function doSth(Model $Model) {
$this->Obj->xyz();
}
Also this might not be the most elegant approach.
You never set the something member of the Document class. You either need to instantiate it inside the constructor, or pass it in.
Personally, I would do something like this:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
$this->behavior->setObject(new MyClass());
}
}
$doc = new Document(..., new SomethingBehavior());
Or better yet, you could even separate it further by doing:
class Document extends AppModel
{
private $behavior;
public function __construct($id,$table,$ids, ModelBehavior $behavior)
{
parent::__construct($id,$table,$ds);
$this->behavior = $behavior
}
}
$behavior = new SomethingBehavior();
$behavior->setObject(new MyClass());
$doc = new Document(..., $behavior);
That way, there is less magic going on in the constructor.

Accessing a protected member variable outside a class

I'm querying for the ID of a field by accessing a class function which someone has already put in place. The result is a object returned with protected member variables. I'm struggling to see how I can access the member variable values outside the class.
Accessing protected or private variables from public is incorrect (thats why they are protected or private). So better is to extend class and access required property or make getter method to get it publicaly. But if you still want to get properties without extending and if you are using PHP 5, you can acces with Reflection classes. Actually try ReflectionProperty class.
class Foo { protected $bar; }
$foo = new Foo();
$rp = new ReflectionProperty('Foo', 'bar');
$rp->setAccessible(true);
echo $rp->getValue($foo);
Here is the correct answer:
We can use bind() or bindTo methods of Closure class to access private/protected data of some class, for example:
class MyClass {
protected $variable = 'I am protected variable!';
}
$closure = function() {
return $this->variable;
};
$result = Closure::bind($closure, new MyClass(), 'MyClass');
echo $result(); // I am protected variable!
Just add a "get" method to the class.
class Foo
{
protected $bar = 'Hello World!';
public function getBar()
{
return $this->bar;
}
}
$baz = new Foo();
echo $baz->getBar();
I'm struggling to see how I can access the member variable values outside the class.
You can't: That's the whole point of protected.
You would have to extend the class with a method that fetches the variables for you.
You can't do this on an instantiated object, though - you would have to influence either the class definition, or change the class of the object at the point it was created.
You can access the protected member of class out side the class, also without extending protected member class, also without using any function of protected member class. Use below function to access it.
function getProtectedMember($class_object,$protected_member) {
$array = (array)$class_object; //Object typecast into (associative) array
$prefix = chr(0).’*’.chr(0); //Prefix which is prefixed to protected member
return $array[$prefix.$protected_member];
}
Please visit the Link to check more details about it.
with closure acces php protected variable for example
class ForExample
{
protected $var=122;
}
$call=function(){
echo $this->var;
};
$call->call(new ForExample());
If you really need that value:
Modify the class and add a public method that returns the value you want.
If you can't modify it, consider extending it and exposing the value there (it will be accessible, since it's protected). Prefer the first option, this is more of a hack.
Clearly, the class designer did not think you'd need the value you're trying to access, otherwise he would have added a method to retrieve it himself. Therefore, reconsider what you're doing.
DISCLAIMER: I don't remember how to code. It's been "a while". This may be completely off.
Well, first of all, if the members are protected, the original designer didn't intend for you to access them directly. Did you check for accessor methods?
If there aren't any, and you're conviced you really need these protected members, you could extend the type with accessors, cast, and get them that way. Like (in C++-like code)
class MyClass : public OldClass
{
public:
int getSomeValue() { return protectedValue; }
void setSomeValue(int value) { protectedValue=value; }
char* getOtherValue() { return otherProtectedValue; }
}
and then to use it
MyClass* blah = (MyClass*)TheirFactory->GiveMeAClass();
int yay=blah->getSomeValue();
You get the drift. Hope this works for you, Internet Explorer makes for a lousy compiler, so I haven't been able to test it.
}

Categories