I have a base class with many sub-classes, and a generic function to cache the results of a function. In the cache function, how do I figure out what sub-class was called?
class Base {
public static function getAll() {
return CacheService::cached(function() {
// get objects from the database
});
}
}
class X extends Base {}
class Y extends Base {}
class Z extends Base {}
class CacheService {
function cached($callback) {
list(, $caller) = debug_backtrace();
// $caller['class'] is always Base!
// cannot use get_called_class as it returns CacheService!
// see if function is in cache, otherwise do callback and store results
}
}
X::getAll();
Z::getAll();
If you're using PHP >= 5.3, you can do this with get_called_class().
Edit: To make it more clear, get_called_class() has to be used in your Base::getAll() method. You, of course, would then have to tell CacheService::cached() what class this reported (adding a method argument would be the most straight-forward way):
class Base {
public static function getAll() {
return CacheService::cached(get_called_class(), function() {
// get objects from the database
});
}
}
class X extends Base {}
class Y extends Base {}
class Z extends Base {}
class CacheService {
function cached($caller, $callback) {
// $caller is now the child class of Base that was called
// see if function is in cache, otherwise do callback and store results
}
}
X::getAll();
Z::getAll();
Try using the magic constant __CLASS__
EDIT: Like this:
class CacheService {
function cached($class, $callback) {
// see if function is in cache, otherwise do callback and store results
}
}
class Base {
public static function getAll() {
return CacheService::cached(__CLASS__, function() {
// get objects from the database
});
}
}
FURTHER EDIT: Using get_called_class:
class CacheService {
function cached($class, $callback) {
// see if function is in cache, otherwise do callback and store results
}
}
class Base {
public static function getAll() {
return CacheService::cached(get_called_class(), function() {
// get objects from the database
});
}
}
Related
I am not sure how to name this, but here it goes. Lets suppose i have the following
class A {
public function aa() {
$this->bb();
}
public function bb() {
}
}
class B extends a {
}
class C {
__construct(B $service) {
$this->service = $service;
}
public function aa() {
$this->service->aa();
}
}
My call in code will be
$C = new C(new B());
$C->aa();
So this will basically execute A:aa() which is what i want. As you can see, in A::aa() AA::bb() is called.
What I need. When AA::bb() is called i want to execute some code defined in class C, but I am not allowed to change the A class. I can only change the B class or the C class.
My idea was to add a listener in the B class and overwrite the bb() function like this
class B extends a {
public $listener;
bb() {
parent::bb();
$this->listener();
}
}
class C {
__construct(B $service) {
$this->service = $service;
}
public function aa() {
$this->service->listener = function() { }
$this->service->aa();
}
}
But I don't like this idea a lot, doesn't look like a good one. What are my options here?
Again, I CANNOT change the A class and i can only call the C class.
PHP version is 5.3
You have two options. Extend or decorate.
First one would be kinda what you have already written, though, I would not use public visibility for the listener:
class Foo extends A {
private $listener;
public function setListener(callable $func) {
$this->listener = $func;
}
public function bb() {
call_user_func($this->listener);
return parent:bb();
}
}
In the example I passed the listener via setter injection, but you can also use constructor injection and pass the $listened in the overloaded __construct() method. When you extend a class, the "interface restriction" does not aply to the constructor's signature.
The other approach is to use a decorator:
class Foo {
private $target;
public function __construct(A $target) {
$this->target = $target;
}
public function bb($callback) {
$callback();
return $this->target->bb();
}
public function __call($method, $arguments) {
return call_user_func_array(
array( $this->target, $method ),
$arguments
);
}
}
The second approach would let you alter the interface.
Which option you pick depend on the exact functionality you actually need to implement. The decorator is a solution for, when you need drastic change in the objects behavior - for example, it is really good for adding access control.
I understand that you want to execute code in C after code in A completes. You cannot change A.
As written, C::aa calls A::aa, which calls A::bb and the stack unwinds. Why not just do the work in C::aa after the service call finishes?
class C {
public function aa() {
$this->service->aa();
// whatever you want to do
}
}
If, on the other hand, you need to call code after A::aa is called but before A::bb is called then the example you posted would suffice with clarity:
class B extends a {
public $listener;
public function bb() {
call_user_func($this->listener);
parent::bb();
}
}
Note the use of call_user_func, which is necessary for PHP 5.3 to call an anonymous function stored in a member variable.
I cant manage to call a static function (with a constant) from a extended class. Here is my code:
(1st file)
class A
{
function isXSet()
{
return X;
}
public static function setX()
{
define('X', 1);
}
}
(second file)
include('/*first file*/');
class B extends A
{
A::setX();
}
How can i manage to do that ?
Your code here
class B extends A
{
A::setX();
}
is a little off. You didn't put your call inside of a method.
class B extends A
{
public static function doSomething() {
A::setX();
}
}
This isn't actually doing anything by means of the parent/child relationship. In fact, after you define class A, the call to A::setX() can happen anywhere since it's public and static. This code is just as valid:
class A
{
function isXSet()
{
return X;
}
public static function setX()
{
define('X', 1);
}
}
class B { // No extending!
function isXSet() {
return A::isXSet();
}
}
What you're more likely looking for is parent instead:
class A
{
public function isXSet()
{
return X;
}
protected static function setX()
{
define('X', 1);
}
}
class B extends A
{
public static function doSomething() {
parent::setX();
var_dump( parent::isXSet() ); // int(1)
}
}
A big plus here is that extending classes can access protected methods and properties from the parent class. This means you could keep everyone else from being able to call A::setX() unless the callee was an instance of or child of A.
Whilst developing an object-orientated HMVC that has a super-object, at some point during the application process, it required the utilisation of namespaces. Here, namespaces will act as a method of "versioning" different code that can be accessed the same way. In the scaled down example below, I am able to execute the class Foo with the method qux if I am in version A or B. I understand that if I utilise self:: rather than $this the problem will disappear, however, I wish to avoid this. At the moment, I get the following PHP error:
Fatal error: Using $this when not in object context
So my question is, how can I use $this in this particular context?
namespace
{
$gamma = new \Gamma();
$gamma->execute('A', 'Foo', 'qux');
// ...
class Alpha
{
// ...
}
class Beta extends Alpha
{
public function foo($input)
{
echo $this->bar($input);
}
public function bar($input)
{
return $input;
}
}
class Gamma extends Beta
{
public function execute($space, $class, $method)
{
call_user_func_array(array($space . '\\' . $class, $method), array());
}
}
}
namespace A
{
class Foo extends \Gamma
{
public function qux()
{
$this->foo('I like turtles');
}
}
}
namespace B
{
class Foo extends \Gamma
{
public function qux()
{
$this->foo('I like strawberries');
}
}
}
The expected output is:
"I like turtles"
Any advice, answers, guidance are much appreciated. :3
Solved.
I was passing the class via the call_user_func_array function statically. Therefore, I was unable to use $this. Thus, an initiation of the requested class would be required, and passed through as a variable, like so:
// ... Continuing from Beta::execute() ...
$class = $space . '\\' . $class;
$class = new $class();
call_user_func_array(array($class, $method), array());
This is what abstract methods are for.
You should declare Master as an abstract class, and qux as an abstract method:
abstract class Master
{
public function __construct()
{
$this->qux();
}
abstract public function qux();
}
class Foo extends Master
{
public function qux()
{
....
}
}
I would like to have a PHPUnit Mock which executes a method like normal, but then modifies the return value in some way before the function returns.
What I have
I have a set of derived classes, similar to below:
abstract class Base
{
abstract protected function getUrl();
public function callUrl() {
$url = $this->getUrl();
// some code to call the URL here
}
}
class Foo extends Base
{
protected function getUrl() {
return "http://www.example.com/Foo";
}
}
class Bar extends Base
{
protected function getUrl() {
return "http://www.example.com/Bar";
}
}
Please note the classes I have are much more complex, and some of the items I have to test have side-effects (such as writing to a database, etc).
The naive, duplicate code approach
If I only had a single derived class (eg; Foo), then I could do the following:
class FooMock extends Foo
{
protected function getUrl() {
return parent::getUrl() . "?sandbox";
}
}
class theTest extends PHPUnit_Framework_TestCase
{
public function testIt() {
$mock = new FooMock();
// assert something
}
}
Unfortunately, this means I would need a specific "Mock" class for each derived class I want to test, all of which perform exactly the same function.
The preferred approach
Instead, I would like to be able to do something like the following:
function callback ($returnValue) {
return $returnValue . "?sandbox";
}
class theTest extends PHPUnit_Framework_TestCase
{
private $mock;
public function testFoo() {
$this->mock = $this->getMockBuilder('Foo')->getMock();
$this->setupMock();
// assert something
}
public function testBar() {
$this->mock = $this->getMockBuilder('Bar')->getMock();
$this->setupMock();
// assert something
}
public function setupMock() {
$this->mock->expects($this->any())
->method('getUrl')
->will($this->postProcessReturnValue('callback'));
}
}
Is this at all possible with PHPUnit?
Update: It was suggested I have an instance of the original class, and an instance of the mock class. Use the original class to get the original return value and modify that. This modified value is then used as the return for the Mock. This is not a feasible way to go about things as the classes are more complex (they have side effects such as writing to the DB).
An example where this would not work;
class Foo extends Base
{
$id = 0;
public function saveToDB() {
$this->id = saveToDBAndReturnId();
}
protected function getUrl() {
if ($this->id > 0) {
return "http://www.example.com/".$this->id;
}
throw new Exception("No ID");
}
}
$foo = new Foo();
$foo->saveToDB();
$url = $foo->getUrl();
Obviously the returned URL would be different between multiple calls. I could always mock saveToDB, but that's starting to feel dirty when all I want to do is post-process the result of getUrl.
PHPUnit allows you to define a stub method that will use a callback to determine what to return.
$this->mock->expects($this->any())
->method('getUrl')
->will($this->returnCallback('callback'));
You can define your callback to call the original class method and modify the return value.
Of course, using mock objects in this way more or less defeats the purpose of having them be "mock" objects, since the mock objects will now rely on the underlying implementation.
I have the following piece of code all over the place:
class Container extends Object implements \IteratorAggregate {
public function AddObject(Object $object, $instanceKey) {
...
}
public function AddComponent(Component $component) {
...
}
}
class MenuContainer extends Container {
public function AddComponent(Menu $component) { // <-- I'm redeclaring the method only because I need to change the typehint
return parent::AddComponent($component);// I don't do anything useful here
}
public function AddObject(Menu $object, $instanceKey) { // <-- I'm redeclaring the method only because I need to change the typehint
return parent::AddObject($object, $instanceKey); // I don't do anything useful here
}
}
I'm forced to do typehint specialization via method redeclaration because I want to prevent people that are using my code from doing mistake and accidentally adding something incompatible to my menu. So the question: Is there a way of doing typehint specialization without the actual method redeclaration?
Instead of specializing your methods, specialize the types you're passing:
class Foo { }
class Bar extends Foo { }
class Container extends Object implements \IteratorAggregate {
public function AddObject(Foo $menu) { }
}
Container::AddObject(new Bar());