Dependency injection an object without all of its methods - php

so a class:
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
its beign created and updated:
$obj = new ToBeUsed();
$obj->setSomething('a');
and passed to another object
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //// !!!!! THIS IS BAD!
}
}
now a classic DI example, except that the passed object should be "dulled" - only some methods are allowed to use. E.g. getSomething() is allowed to use, but setSomething() is not. What pattern / practice can get away with it? There used to be friend classes is C but its Php...

class ToBeUsed
{
private $a;
public function setSomething($a)
{
$dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
if(count($dbg) > 1){
return;
}
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
echo $this->obj->getSomething().PHP_EOL; // a
$this->obj->setSomething('b'); // this does nothing
echo $this->obj->getSomething().PHP_EOL; // a
}
}
$obj = new ToBeUsed();
$obj->setSomething('a');
$obj2 = new UseIt($obj);
$obj2->work();
Alternatively, you can perform more complex checks on debug_backtrace() output.

I would probably do something with Interfaces, it doesn't prevent a method form being used. But "they" (whoever they is) would be using it outside of the Interface for $obj.
Like this:
class ToBeUsed implements ToBeUsedInterface
{
private $a;
public function getSomething()
{
return $this->a;
}
public function setSomething($a)
{
$this->a = $a;
}
}
interface ToBeUsedInterface{
public function getSomething();
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsedInterface $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //This now exists outside of the interface for $obj
}
}
In terms of IDE's this would prevent the methods from auto-completing as well.
The only other thing I can think of, ( besides the other answer ) would be to set the method to protected and then use ReflectionMethod to change the viability, when you want to use it.
Another Option, is Using Reflection
class ToBeUsed
{
private $a;
public function getSomething()
{
return $this->a;
}
protected function setSomething($a)
{
$this->a = $a;
}
}
$ToBeUsed = new ToBeUsed();
$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');
echo $ToBeUsed->getSomething();
Outputs:
foo
You can see it live here
And Obviously sense it's protected under normal conditions, it could not be used inside UseIt. If I was going to use this for any amount of code, I would extend or wrap the Reflection class. Just to make the call a bit more concise, like this:
class MyReflector
{
public static function invoke($class, $method, ...$args)
{
$ReflectionMethod = new ReflectionMethod($class, $method);
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invokeArgs($class, $args);
}
}
$ToBeUsed = new ToBeUsed();
MyReflector::invoke($ToBeUsed,'setSomething', 'foo');
Please note I got all fancy with the variadic ...$arg which is for PHP 5.6+ it just lets you do
MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');
And $args would be ['foo','bar'] in the first example it's just ['foo'] which can be used for invokeArgs for the second argument which takes an array of arguments to pass on to the actual method.

Related

PHP How to refer to a class method definition?

I would like to bind a function (a method of a class) to another class. Any idea of how i could achieve this?
Here is an example of what i want:
class A {
protected $prop = "prop A";
function method($arg1, ...) {
return $this->prop;
}
}
class B {
protected $prop = "prop B";
// need help here
}
So i want to "bind" the method "method" of class "A" to class "B" so it'll be possible to do $b = new B(); $b->method($arg1, ...); and obtain "prop B";
Thanks in advance!!
I tried:
class B {
protected $instance;
protected $prop = "prop B";
public function __construct($instance) {
$this->instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array([$this->instance, $method], $args);
}
}
$b = new B(new A());
$b->method();
But still outputing "prop A";
I tried this too:
class B {
protected $prop = "prop B";
public function __call($method, $args) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
$a = new A();
$b = new B();
$b->method = $a->method;
$b->method();
But i'm getting this error: Closure::bind() expects parameter 1 to be Closure, null given....
At last i tried this too:
class B {
protected $instance;
protected $prop = "prop B";
public function __construct($instance) {
$this->instance = $instance;
}
public function __call($method, $args) {
$new_method = $this->instance->$method->bindTo($this);
return call_user_func_array($new_method, $args);
}
}
$b = new B(new A());
$b->method();
Here, an error says $this->instance->$method is null
Using dependency injection, you can access the passed object:
class A {
public function speak() { return ‘meow’; }
}
class B {
public function __construct($obj) {
$this->a = $obj;
}
}
$demo = new B( new A));
print $demo->a->speak();
// => meow
From here, if you want B->speak to refer to A->speak:
class A {
public function speak() { return ‘meow’; }
}
class B {
public function __construct($obj) {
$this->a = $obj;
}
public function speak() {
return $this->a->speak();
}
}
$demo = new B( new A));
print $demo->a->speak(); // meow
print $demo->speak(); // meow
Or, if B is a special kind of A:
// use obj A above
class B extends A {
// do stuff B knows
}
$demo = new B;
print $demo->speak();
// => meow
If you’re wanting the exact same method in both classes, perhaps what you’re looking for is traits. Traits are more or less an include for objects which lets objects “share” code. (Personally I think it’s a fancy way of violating DRY and is better handled with DI... but smarter people than I have included it in the language)
Using traits would be something like this (double check docs, I’ve never used this)
trait sharedMethod {
public function speak() {
return $this->prop;
}
}
class A {
use sharedMethod;
public $prop = “I’m from A”;
}
class B {
use sharedMethod;
public $prop = “I’m from B”;
public function __construct(object $a) {
$this->a = $a;
}
/**
* use this to get A’s method, or omit to keep B’s method
*/
public function speak() {
return $this->a->speak();
}
}
$demo = new B( new A));
print $demo->speak(); // I’m from A
// if you don’t override the method
$demo = new B( new A));
print $demo->speak(); // I’m from B

Cannot re-assign $this: Use reference of an existing object A into another object B which class extends the one of object A

In PHP (Symfony 3);
I want to reference an existing object A in another object B which class extends the one of object A, like this:
class A {
private $property1;
private $property2;
public function __construct($p1,$p2){
$this->property1 = $p1;
$this->property2 = $p2;
}
}
class B extends A {
private $property3;
public function __construct($objectA,$p3){
$this = $objectA;
$this->property3 = $p3;
}
}
$a = new A('p1','p2');
$b = new B($a,'p3');
This does not work and throw the following error at the statement $this = $objectA:
Compile Error: Cannot re-assign $this
Which are documented and explain there and there. I am looking for a workaround.
You must call parent constructor and also make property1 and property2 visible in class B
<?php
class A {
private $property1;
private $property2;
public function __construct($p1,$p2){
$this->property1 = $p1;
$this->property2 = $p2;
}
public function getProperty1()
{
return $this->property1;
}
public function getProperty2()
{
return $this->property2;
}
}
class B extends A {
private $property3;
public function __construct($objectA,$p3){
parent::__construct($objectA->getProperty1(), $objectA->getProperty2());
$this->property3 = $p3;
}
}
$a = new A('p1','p2');
$b = new B($a,'p3');
See it live here: http://sandbox.onlinephpfunctions.com/code/705bf1827da2bdf10f8d961ee1cb6fbdd88bc663
As an alternative, you could use __call magic method to forward all cals to class A:
<?php
class A {
private $property1;
private $property2;
public function __construct($p1,$p2){
$this->property1 = $p1;
$this->property2 = $p2;
}
}
class B extends A {
private $property3;
private $a;
public function __construct($objectA,$p3){
$this->a = $objectA;
$this->property3 = $p3;
}
public function __call($name, $arguments)
{
return call_user_func_array(array($this->a, $name), $arguments);
}
}
$a = new A('p1','p2');
$b = new B($a,'p3');
Based on how to clone object to child class in php
Using get_object_vars on the parent object, you can get an array of properties keys and values. You can then loop through them and assign them to the child object:
<?php
class A {
protected $property1;
protected $property2;
public function __construct($p1,$p2){
$this->property1 = $p1;
$this->property2 = $p2;
}
}
class B extends A {
private $property3;
public function __construct($objectA,$p3){
//$this = $objectA;
$objValues = get_object_vars($objectA); // return array of object values
foreach($objValues AS $key=>$value)
{
$this->$key = $value;
}
$this->property3 = $p3;
echo $this->property1;
}
}
$a = new A('p1','p2');
$b = new B($a,'p3');
This does not work with private properties, they need to be at least of protected level.
I ended up managing it like that:
class B extends A{
public function __construct($objectA){
foreach($this as $k => $v){
if(isset($objectA->{$k})){
$this->{$k} = &$objectA->{$k};
}
}
}
}
Compare to #Antony answer, notice it has & in front of $objectA->{$k}: $this->{$k} = &$objectA->{$k};. As I understood it, with &, any change on $objectB of properties belonging to the extended class A applies to $objectA.
I am aware it is not perfect and quite hacky but it does the job I need. Thanks for the input given by everybody.

Creating a new class object and setting variables directly - PHP

I am trying to understand how to efficiently create a new class object and set the variables directly.
I have a class:
class element_model
{
public $sType;
public $properties;
}
I have a controller in which the following function is defined:
public function create_element($sType, $properties)
{
$oElement_model = new element_model($sType, $properties);
return new element_model($sType, $properties);
}
But this does not returns a new element_model with properties set, it just returns an empty object.
It does not, however, throw an error.
What is the reason the function above does not work?
You have to pass to the constructor of the class, in PHP you should have a method in the class __construct :
class element_model
{
public $sType;
public $properties;
public function __construct($type, $property)
{
$this->sType = $type;
$this->properties = $property;
}
}
Then you can access them (note the variables are public)
$elem = new element_model($sType, $properties);
$elem->sType;
Although in some cases it is better to encapsulate vars (declare them private):
class element_model
{
private $sType;
private $properties;
public function __construct($type, $property)
{
$this->sType = $type;
$this->properties = $property;
}
public function getType()
{
return $this->sType;
}
public function getProperty()
{
return $this->properties;
}
}
Then you can access the variable through a getter
$elem = new element_model($sType, $properties);
$elem->getType(); //and
$elem->getProperty();
You must create a __construct function in your class that accepts the parameters and sets your variables. Like this:
class element_model{
.
.
.
public function __construct($type,$properties)
{
$this->sType = $type;
$this->properties = $properties;
}
}
The __construct function will be called when you create the object.
But if you want to be extra cool in programming, just define your properties as private and create getter and setter functions to access the variables of your object
private $sType;
public function getSType(){
return $this->sType;
}
public function setSType($value){
$this->sType = $value;
}

PHP Observer Pattern, Issue

Bellow is a PHP script.
I tried to implement the Observer pattern (without MVC structure)... only basic.
The error which is encountered has been specified in a comment.
First I tried to add User objects to the UsersLibrary repository. There was a error such as User::update() does not exists or something.
Why is that error encountered? What fix should be applied and how?
interface IObserver {
public function update(IObservable $sender);
}
interface IObservable {
public function addObserver(IObserver $obj);
public function notify();
}
class UsersLibrary implements IObservable {
private $container;
private $contor;
//private $z;
public function __construct() {//IObserver $a) {
$this->container = array();
$this->contor = 0;
echo "<div>[constructing UsersLibrary...]</div>";
$this->addObserver(new Logger());
//$this->z = $a;
}
public function add($obj) {
echo "<div>[adding a new user...]</div>";
$this->container[$this->contor] = $obj;
$this->contor++;
$this->notify();
}
public function get($index) {
return $this->container[$index];
}
public function addObserver(IObserver $obj) {
$this->container[] = $obj;
}
public function notify() {
echo "<div>[notification in progress...]</div>";
foreach($this->container as $temp) {
//echo $temp;
#################################################################
$temp->update(); //--------ERROR
//Fatal Error: Call to a member function update() on a non-object.
#################################################################
}
//$this->container[0]->update();
//$this->z->update($this);
}
}
class User {
private $id;
private $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
}
class Logger implements IObserver {
public function __construct() {
echo "<div>[constructing Logger...]</div>";
}
public function update(IObservable $sender) {
echo "<div>A new user has been added.</div>";
}
}
$a = new UsersLibrary(); //new Logger());
//$a->add(new User(1, "DemoUser1"));
//$a->add(new User(2, "DemoUser2"));
$a->add("Demo");
echo $a->get(0);
//echo $a->get(0)->getName();
Your User class is not implementing interface IObserver and therefore is not forced to have the method update().
You have to instantiate a new User() in order to add it to the UsersLibrary:
$library = new UsersLibrary();
$user = new User(1, "Demo");
$library->add($user);
Also, you are mixing Users and Loggers into your UsersLibrary container. Maybe think about separating the containers for them?
You are passing a string instead of an object in your $a->add() call. You should either pass in an object, or alter the code in UserLibrary::add() to wrap it's argument in an appropriate object (or do an object lookup of it sees a string, for instance find a user with that name).
$user = new User(1, "Demo");
$a = new UsersLibrary();
$a->add($user);

PHP getter/setter to array

Following "problem"
PHP Class with a lot of propertys. A lot of Getters / Setter.
Is there any nice solution to convert all propertys to an array?
protected $name;
protected $date;
public function getName();
public function getDate();
public function asArray(); // call all getters?
Is your API already defined and are you stuck with getX and setX methods? I much prefer properties. Less typing, better distinction between properties and methods, and resulting code looks more like PHP and less like Java. But exposing properties doesn't mean you lose encapsulation and make all your internals public. With __get and __set magic methods you can have pretty fine-grained control over what you present. Plus, it would be rather trivial to dump the properties as an array:
class Foo
{
protected $properties;
public function __construct() {
$this->properties = array();
}
public function __set($prop, $value) {
$this->properties[$prop] = $value;
}
public function __get($prop) {
return $this->properties[$prop];
}
public function toArray() {
return $this->properties;
}
}
Alas, if you're stuck with setters/getters because of a cranky boss or some misunderstanding of what OOP must be, why not just cast the object to an array?
class Bar
{
public $x;
public $y;
public $z;
protected $a;
protected $b;
protected $c;
private $q;
private $r;
private $s;
public function __construct() {
}
public function setA($value) {
$this->a = $value;
}
public function getA() {
return $this->a;
}
public function setB($value) {
$this->b = $value;
}
public function getB() {
return $this->b;
}
public function setC($value) {
$this->c = $value;
}
public function getC() {
return $this->c;
}
public function toArray() {
return (array)$this;
}
}
Notice how public, protected, and private properties are cast:
$bar = new Bar();
print_r($bar->toArray());
array(9) {
["x"]=>
NULL
["y"]=>
NULL
["z"]=>
NULL
[" * a"]=>
NULL
[" * b"]=>
NULL
[" * c"]=>
NULL
[" Foo q"]=>
NULL
[" Foo r"]=>
NULL
[" Foo s"]=>
NULL
}
Note that the array keys for protected/private don't start with a space, it's a null. You can re-key them, or even filter out protected/private properties if you like:
public function toArray() {
$props = array();
foreach ((array)$this as $key => $value) {
if ($key[0] != "\0") {
$props[$key] = $value;
}
}
return $props;
}
You're working with a dynamic language; take advantage of that and enjoy it!
How about using ReflectionClass and ReflectionMethod, something like this:
class PropertyHolder
{
private $name;
private $date;
private $anotherProperty;
public function __construct($name, $date)
{
$this->name = $name;
$this->date = $date;
}
public function getName()
{
return $this->name;
}
public function getDate()
{
return $this->date;
}
public function asArray()
{
$result = array();
$clazz = new ReflectionClass(__CLASS__);
foreach ($clazz->getMethods() as $method) {
if (substr($method->name, 0, 3) == 'get') {
$propName = strtolower(substr($method->name, 3, 1)) . substr($method->name, 4);
$result[$propName] = $method->invoke($this);
}
}
return $result;
}
You could use PHP's reflection capabilities. Here's an example:
http://www.weberdev.com/get_example-4672.html
Try looking into get_object_vars(), get_class_vars and others in the same category. The examples shown there look like pretty much what you need. Check the comments there (for example http://www.php.net/manual/en/function.get-class-vars.php#87772) they already provide ways that suit your needs.
A simple (array) cast on $this will suffice:
(array) $this;
If you have additional properties (private ones for example, that shall not be toArray()ed) you can afterwards unset these:
public function toArray() {
$array = (array) $this;
unset($array['private'], $array['privateagain']);
return $array;
}
One option would be to create an array in your constructor.
You will have one getter and one setter..
When you want to set or get something, do something like:
$foo->get( 'UID' ); //(to get user id)
or
$foo->set( 'UID', 5 ); // to set something)

Categories