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.
Related
My coworker ask me how to dynamically implement methods in a class. What I come up with was strategy pattern. At the first time, I made regular strategy pattern, and at the end I understood it's not good idea to make property call function. Because the child class is controller class whose methods needs to be called directly.
So, I'm trying to implement Package's method to B class directly. But I'm stuck when calling __call function. The function tried to implement works in class B. However, when it's extended the function I saved in B class doesn't work.
<?php
class A {
public $once = 0;
}
class B extends A {
public $methods = [];
public function __construct()
{
//load package
$package = new Package;
//get method names
$methods = get_class_methods($package);
//loop to assign methods to class B instance
foreach($methods as $method)
{
$this->methods[$method] = $this->setFunc($package, $method);
}
}
//I made this function because [$package, $method] was regarded as
// array instead of function when it is assigned to other variable
private function setFunc($package, $methodName)
{
return function() use($package, $methodName)
{
return call_user_func([$package, $methodName]);
};
}
}
//package class
class Package {
public function hello_world(){
return "hello world";
}
}
class C extends B{
public function __construct()
{
parent::__construct();
//assigning functions to class C
foreach($this->methods as $key => $val)
{
//I did it in child class because dynamically produced properties
// weren't recognized
$this->$key = $val;
}
}
//dynamically assigned functions to dynamic properties must be called by
//__call function
public function __call($name, $arguments)
{
//I made this condition because calling function loops infinitely.
if($this->once >= 1)
{
return;
}
$this->once++;
//not working here. nothing shown
return $this->$name();
}
}
$c = new C;
echo $c->hello_world(); //I want to display hello world!
replace return $this->$name(); with call_user_func($this->$name,[]);
or in php7 this works return ($this->$name)();
I'm new to object oriented php. And if there are no functions in the method testing() in the HumanClass, should i declare them as abstract?
<?php
class HumanClass
{
private $legs;
private $hands;
public function __construct($legs, $hands)
{
$this->legs = $legs;
$this->hands = $hands;
}
public function testing()
{
}
}
class StudentClass extends HumanClass
{
private $books;
public function __construct($legs, $hands, $books)
{
parent::__construct($legs, $hands);
$this->books = $books;
}
public function testing()
{
echo "StudentClass called.";
}
}
function callClass(HumanClass $c)
{
$c->testing();
}
$example = new StudentClass(4, 2, 1);
callClass($a);
?>
Is it possible to have something like this?
echo $a->testing();
instead of having another method to call testing().
Given the code that you give, it's far from clear what the testing() function is supposed to do other than just exist for you to try things. The answer to that will also determine whether the versions in the baseclass should remain there as empty function.
There are other options, too, e.g. that the derived class first invokes the baseclass (extending), or that the baseclass doesn't contain an abstract or concrete such function but only the derived one does. Which to choose is up to the informed programmer to decide.
I need to organize some kind of access control to object methods when it is used in different contexts (API's in my system). Here is code example:
class A
{
public function doA(){}
public function doB(){}
}
class APIAClient
{
public function getA()
{
return new A();
}
}
class APIBClient {
public function getA()
{
return new A();
}
}
In APIAClient object A should have both methods doA() and doB(), but in APIBClient should not have doB() method.
For now I've implemented APIBClientAProxy (which is returned by APIBCleint->getA())
class APIBClientAProxy
{
private $a = new A;
public function doA()
{
$this->a->doA()
}
}
But may be there is a better pattern for solving my problem, without using a additional proxy object for every context (i.e. API). I'm thinking about magic __call method with list of allowed methods in particular context, but magic calls is hard do document and documentation is the big point in my app (API's should be documented well)
Thanks!
Instead of inheritance you can use composition through traits (introduced in PHP 5.4).
First define traits
trait A {
public function doA() {
// do something here
}
}
trait B {
public function doB() {
// do something here
}
}
then use those traits in your class declaration
class APIAClient {
use A, B
}
class APIBClient {
use A
}
You could use inheritance here, like this:
class A {
public function doA() {
// do something here
}
}
class B extends A {
public function doB() {
// do something here
}
}
class APIAClient
{
public function getObj() {
return new B();
}
}
class APIBClient {
public function getObj() {
return new A();
}
}
This way, when you call getObj() on APIAClient, it will return an instance of B which which has both doA() and doB(). However, when you call it on APIBClient, you return an instance of A which only has doA().
You can't change the class depending on when and how it's instances are created (well, not really). You could use a hacky workaround (but I'd recommend against it)
class A
{
private $_canDoB = null;
public function __construct($doB = true)
{
$this->_canDoB = !!$doB;//force bool
}
public function doB()
{
if ($this->_canDoB === false)
{
throw new LogicError('You can\'t doB');
}
}
}
So if you pass a falsy value to the constructor of A(in your APIBClient), doB will throw an error. However, I'd recommend using inheritance, too:
class AB
{
public function doA()
{
//both B and B share this method
}
}
class B
{//nothing atm
}
class A
{
public function doB()
}
And have your APIAClient return a new A(), whereas APIBClient returns a new instance of the B class.When using type-hinting, you can just check for AB instances:
public function doSomething(AB $instance)
{
if ($instance instanceof A)
{
return $instance->doB();
}
return $instance->doA();
}
Or, when not relying on type-hinting and type-checking, you can always use one of the many functions like method_exists
I have three classes. Class A, Class B, Class C. What I am trying to do, send a request to Class B form Class A, and Class B must redirect that request to Class c.
May be a simple example from below will give a certain idea.
class classa {
public function __construct() {
$obj_classb = new classb;
$obj_classb -> someRequest(); // This request must go to Class B and query the Class C
}
}
class classb {
//This class must do something, which is going to redirect any sorts of request it receives to the next classc
}
class classc {
public function someRequest() {
//do whatever
}
}
Any Idea?
You can create a "redirector" class by overriding the __call method like this:
class classb {
private $obj_classc;
public function __construct() {
$this->obj_classc = new classc;
}
public function __call($name, $arguments) {
return call_user_func_array(array($this->obj_classc, $name), $arguments);
}
}
Of course this will "forward" only method calls; if you are interested in forwarding property getters/setters etc you will have to override more magic methods.
Choosing the forwarding target can also be arranged (in this example it's just an automatically-created classc object; but you can pass it as a parameter in the constructor or provide it in any other way you choose).
Update: Magic functions you need to override to forward property accesses:
public function __set($name, $value) {
$this->obj_classc->$name = $value;
}
public function __get($name) {
return $this->obj_classc->$name;
}
public function __isset($name) {
return isset($this->obj_classc->$name);
}
public function __unset($name) {
unset($this->obj_classc->$name);
}
sorry for that weird subject but I don't know how to express it in an other way.
I'm trying to access a method from a calling class. Like in this example:
class normalClass {
public function someMethod() {
[...]
//this method shall access the doSomething method from superClass
}
}
class superClass {
public function __construct() {
$inst = new normalClass;
$inst->someMethod();
}
public function doSomething() {
//this method shall be be accessed by domeMethod form normalClass
}
}
Both classes are not related by inheritance and I don't want to set the function to static.
Is there any way to achieve that?
Thanks for your help!
You can pass a reference to the first object like this:
class normalClass {
protected $superObject;
public function __construct(superClass $obj) {
$this->superObject = $obj;
}
public function someMethod() {
//this method shall access the doSomething method from superClass
$this->superObject->doSomething();
}
}
class superClass {
public function __construct() {
//provide normalClass with a reference to ourself
$inst = new normalClass($this);
$inst->someMethod();
}
public function doSomething() {
//this method shall be be accessed by domeMethod form normalClass
}
}
You could use debug_backtrace() for this. It is a bit iffy but for debugging purposes it is usefull.
class normalClass {
public function someMethod() {
$trace = debug_backtrace();
$trace[1]['object']->doSomething();
}
}
You have a few options. You can use aggregation like so
class normalClass
{
protected $superClass;
public function __construct( superClass $superClass )
{
$this->superClass = $superClass;
}
public function someMethod()
{
$this->superClass->doSomething();
}
}
class superClass
{
public function __construct()
{
$inst = new normalClass( $this );
$inst->someMethod();
}
public function doSomething()
{ //this method shall be be accessed by domeMethod form normalClass
}
}
Or just a straight-up setter
class normalClass
{
protected $superClass;
public function setSuperClass( superClass $superClass )
{
$this->superClass = $superClass;
}
public function someMethod()
{
if ( !isset( $this->superClass ) )
{
throw new Exception( 'you must set a superclass' );
}
$this->superClass->doSomething();
}
}
class superClass
{
public function __construct()
{
$inst = new normalClass();
$inst->setSuperClass( $this );
$inst->someMethod();
}
public function doSomething()
{ //this method shall be be accessed by domeMethod form normalClass
}
}
Depending on your use case, you might want to pass the instance to the function only:
class normalClass {
public function someMethod($object) {
$object->doSomething();
}
}
If normalClass::someMethod() can be called by multiple, distinct $objects, this might be the better choice (instead of providing the $object to the whole normalClass instance).
But regardless of that you might consider creating an Interface to use for type hinting:
interface ISomethingDoer {
public function doSomething();
}
class normalClass {
public function someMethod(ISomethingDoer $object) {
# Now PHP will generate an error if an $object is passed
# to this function which does not implement the above interface.
// ...
class superClass implements ISomethingDoer {
// ...
woah I had the same problem than you but instead of going with the so simple pass the reference to the object, I went with an event manager, Basically, when something would happen in the normal class, it would trigger an event which was listened by a class and that said class(the listener) would call the super class to execute that functionality and if necessary pass it new arguments.
Anyways, whether you pass it as a parameter to your object or you go with an event based approach, both solutions work. Choose the one you prefers.
For more information on events, sympony explains it quite good.
http://symfony.com/doc/current/components/event_dispatcher/introduction.html