I'm trying to change a property in my parent class with my child class but I'm not getting the result I'm expecting.
I've done some research (like Change parent variable from child class), but I can't seem to find the problem.
class A {
public $msg;
public function __construct() {
$this->msg = 'foo';
}
public function setMessage($string) {
$this->msg = $string;
}
public function getMessage() {
var_dump($this->msg); // For demo purposes
}
public function triggerB() {
$b = new B;
}
}
class B extends A {
public function __construct() {
parent::setMessage('bar');
}
}
$a = new A;
$a->getMessage();
$a->triggerB();
$a->getMessage();
The output I get is "foo" twice and I expect it to be "foo" "bar".
Could anyone explain me what i'm doing wrong and how I can fix this?
In my actual code I want the child-class to validate some $_POST values, and return the outcome to the Main-class. The parent uses the child to validate.
You are having your object A and creating an instance of it and storing it in the variable $a, in the global scope. And then you are creating another instance of your class B and storing it in a variable $b which is in the scope of the method triggerB().
You can only change the properties of the parent class A if you pass an argument to your another class B.
So something like this should suffice:
<?php declare(strict_types = 1);
class A {
public $msg;
public function __construct() {
$this->msg = 'foo';
}
public function setMessage(string $string) {
$this->msg = $string;
}
public function getMessage() {
var_dump($this->msg); // For demo purposes
}
public function triggerB() {
(new B($this));
}
}
class B {
public function __construct(A $a) {
$a->msg = "bar";
}
}
$a = new A;
$a->getMessage();
$a->triggerB();
$a->getMessage();
This approach is better suited to readability and better dependency management.
Another approach:
<?php declare(strict_types = 1);
class A {
public $msg;
public function __construct() {
$this->msg = 'foo';
}
public function setMessage(string $string) {
$this->msg = $string;
}
public function getMessage() {
var_dump($this->msg); // For demo purposes
}
public function triggerB() {
$this->msg = 'bar';
}
}
class B {
public function __construct(A $a) {
$a->msg = "bar";
}
}
$a = new A;
$a->getMessage();
$a->triggerB();
$a->getMessage();
This is performance wise better, but if you are going to be doing something complex, the first method is better.
Note: The above code is for PHP7.
Your triggerB() method does not actually do anything:
public function triggerB() {
$b = new B;
}
You are creating a new object and assign that to the $b variable. As soon as the method finishes, the $b variable / object ceases to exist.
Also note that the $b variable in your method is is no way related to the $a variable in the global scope so setting any of its properties has no influence on $a.
Related
My question would be best illustrated by the following example:
class a
{
function a()
{
return file_get_contents('http://some/third/party/service');
}
}
class b
{
function b()
{
$a = new a();
return $a->a() . ' Bar';
}
}
class testB extends test
{
function testB()
{
$b = new b();
// Here we need to override a::a() method in some way to always return 'Foo'
// so it doesn't depend on the third party service. We only need to check
// the $b::b() method's behavior (that it appends ' Bar' to the string).
// How do we do that?
$this->assert_equals('Foo Bar', $b->b());
}
}
Let me point out that I don't have the control over where class 'a' is being defined/included.
If you changed class b so that the instance of a can be passed in:
class b
{
function b($a = null)
{
if ($a == null) {
$a = new a();
}
return $a->a() . ' Bar';
}
}
...then for test, you can use a framework like Mockery to pass in a mocked instance of 'a' which always returns 'Foo'
use \Mockery as m;
class testB extends test
{
public function tearDown()
{
m::close();
}
public function testB()
{
$mockA = m::mock('a');
$mockA->shouldReceive('a')->times(1)->andReturn('foo');
$b = new b($mockA);
$this->assert_equals('Foo Bar', $b->b());
}
}
See the full docs and examples for Mockery here: http://docs.mockery.io/en/latest/getting_started/simple_example.html
You can eliminate your dependency like that:
First you create an interface that will list all methods you need:
interface Doer {
function a();
}
Then create an adapter class for you a class:
class ADoer implements Doer
{
protected $dependencyA;
public function __construct(A $dep) {
$this->dependencyA = $dep;
}
public function a() {
$this->dependencyA->a();
}
}
Now make your B class depends on Doer interface, not on A implementation:
class B {
private $doer;
public function __construct(Doer $a) {
$this->doer = $a;
}
public function b() {
$this->doer->a();
}
public function setDoer(Doer $a) {
$this->doer = $a;
}
//getDoer()
}
Now you can switch it at will:
class FooDoer implements Doer {
function a() {
//do whatever you want
}
}
$b->setDoer(new FooDoer());
$b->b();
I'd like do do this:
class A {
public static $var = 'foo';
}
class B {
private $a;
public function __construct($a) {
$this->a = $a;
}
public function f() {
echo $this->a::$var; // <---- ERROR
echo get_class($this->a)::$var; // <---- ERROR
// WORKING but awful
$a = $this->a;
echo $a::$var;
}
}
$b = new B(new A());
$b->f();
Note that I don't know if $a is an instance of A or another class, I just know that it has a static $var member. So, unfortunately, I can't use A::$var inside f.
Does anyone know if there is a single-expression syntax to do this, using PHP 5.3+?
You're passing an instance of A into B, but $var is a static Member of A which can only be accessed using :: like A::$var.
private function f() {
echo A::$var;
}
EDIT:
if you want to make it dependant of the instance stored in $a you could do something like:
public function f() {
$class = get_class($this->a);
echo $class::$var;
}
First thing I'm seeing is that you couldn't call f() as it is private.
$b->f();
Then maybe you can turn to something like this?
class B extends A{
private $a;
public function __construct($a) {
$this->a = $a;
}
public function f() {
echo static::$var;
}
}
The following will work but I'd question why you'd want to do this:
class A {
public static $var = 'foo';
}
class B {
private $a;
public function __construct($a) {
$this->a = $a;
}
public function f() {
if ($this->a instanceof A) {
echo A::$var;
}
}
}
$b = new B(new A());
$b->f();
you don't need to do anything specifically with $this->a as $var is static to the class.
You could write a function like so:
function get_static_property($class, $property) {
return $class::$$property;
}
I can't help but wonder why you would need this. Any reason why you can't have A implement an interface? Or use a trait?
That way you can just echo $this->a->someMethodName().
PS. returning strings is generally preferred over echoing inside methods.
This's my second question, even thought, i answered the previous one, on my own. Anyway, I have a basic problem with OOP, on how to call a non-static method from another class. example:
We have a class named A in a file A.class.php
class A {
public function doSomething(){
//doing something.
}
}
and a second class named B on another file B.class.php
require_once 'A.class.php';
class B {
//Call the method doSomething() from the class A.
}
I think now it's clearn. How to : Call the method doSomething() from the class A ?
Class B will need an object of Class A to call the method on:
class B {
public function doStuff() {
$a = new A();
$a->doSomething();
}
}
Alternatively, you can create the instance of A outside of B and pass it into B's constructor to create a global reference to it (or pass it to an individual method, your choice):
class B {
private $a = null;
public function __construct($a) {
$this->a = $a;
}
public function doStuff() {
$this->a->doSomething();
}
}
$a = new A();
$b = new B($a);
How about injecting class A into B, making B dependant on A. This is the most primitive form of dependency injection:
class A
{
public function doSomething()
{
//doing something.
}
}
class B
{
private $a;
public function __construct( A $a )
{
$this->a = $a;
}
//Call the method doSomething() from the class A.
public function SomeFunction()
{
$this->a->doSomething();
}
}
This is constructed like this:
$a = new A();
$b = new B( $a );
You need to instantiate a an object of class A. You can only do this inside a method of class B.
class B{
public function doSomethingWithA(){
$a = new A();
return $a->doSomething();
}
}
class B {
public function __construct()
{
$a = new A;
$a->doSomething();
}
}
I know this is an old question but considering I found it today I figured I'd add something to #newfurniturey's answer.
If you wish to retain access to class B within class A this is what I did:
class A
{
private $b = null
public function __construct()
{
$this->b = new B($this);
if (!is_object($this->b) {
$this->throwError('No B');
}
$this->doSomething();
}
public function doSomething() {
$this->b->doStuff();
}
private function throwError($msg = false) {
if (!$msg) { die('Error'); }
die($msg);
}
}
class B {
public function doStuff() {
// do stuff
}
}
This is constructed like this:
$a = new A();
I have a class Foo with a number of public and private methods. One of those methods is getting rather large, and I would like to fork it off into a separate class specifically for that purpose. Something like this:
<?php
class Foo
{
// ...
public function doX( $a, $b )
{
$x = new FooXDoer;
$x->foo = $this;
return $x->run( $a, $b );
}
// ...
}
class FooXDoer
{
public $foo;
public function run( $a, $b )
{
// ...
}
// ...
}
FooXDoer has access to Foo's public methods and properties through $this->foo.
How can I give FooXDoer access to Foo's private methods and properties, without making them public to the rest of the code which already uses Foo?
Should I create a separate class FooPrivate which has the private methods as public, and which Foo wraps, and then have FooXDoer reference that? What should FooPrivate be called?
Or is my approach completely wrong? How do you solve this problem in general?
Looks like traits solve your problem best in case you use PHP >= 5.4.
If not, I thought of the following solution:
class A {
private static $allowedClasses = array('B');
private $a = 1;
public function __get($property) {
$caller = debug_backtrace(false);
if(!isset($caller[1]))
throw new Exception('Bla bla');
if(!in_array($caller[1]['class'], self::$allowedClasses))
throw new Exception('Bla bla');
return $this->$property;
}
public function testB() {
$b = new B();
$b->instA = $this;
echo $b->getA();
}
}
class B {
public $instA;
public function getA() {
return $this->instA->a;
}
}
class C {
public function getA() {
$instA = new A();
return $instA->a;
}
}
$a = new A();
$a->testB(); // Works ok;
$c = new C();
$c->getA(); // Throws exception here;
This code is definitely not a best practice :) But since it is possible I put it here.
PHP has no friend class concept, from what I've read I wouldn't say it's a bad decision by the php designers...
IMHO, there is no general strategy, as the problem or question is too broad: there are too many factors to consider:
how many private properties and methods of Foo are needed in run()?
from an abstraction point of view: how closely is run() entangeled in Foo? Does it really "deserve" to be in an independent class?
will you ever use FooXDoer outside of Foo?
Two ideas for solutions:
hand over the needed data from foo to fooDoer, either value for value or by implementing a compileRunData() on Foo that returns an array or an object
public function doX( $a, $b )
{
$x = new FooXDoer;
$workEnvironment = $this->compileRunData();
$x->setEnvironment( $workEnvironment );
$x->foo = $this;
return $x->run( $a, $b );
}
or use inheritance, especially the concept of protected properties:
abstract class FooAbstract
{
protected $_basicVar1;
protected function basicMethod1(/*...*/) {
//...
}
// ...
}
abstract class FooRunner extends FooAbstract
{
protected $_runVar1;
protected function runMethod1(/*...*/) {
//...
}
public function run($a, $b) {
// ...
}
}
public class Domain_Model_Foo extends FooRunner
{
}
edit: hey, SO didn't show me there was already an answer. Yea, thought about traits, too, but haven't used them until now so can't really comment on them
I'm a totally newbie in class writing in PHP, started a couple days ago.
I'd like to declare a new "public property" inside a method to be used in other methods.
This is what I thought (Of course it doesnt' work!):
class hello {
public function b() {
public $c = 20; //I'd like to make $c usable by the method output()
}
public function output() {
echo $c;
}
}
$new = new hello;
$new->output();
Thanks in advance for any tips.
I'd like to declare a new "public property" inside a method to be used in other methods.
If the other methods are part of the same class, you don't need a public property, a private property will fit your needs. Private properties are accessible within the same class only which helps to keep things simple.
Also understand the difference between declaring a property and assigning a value to it. Declaring is done when the code is loaded, assigning when it executes. So declaring (or defining) a property (private or public) needs a special place in the PHP syntax, that is in the body of your class and not inside in a function.
You access properties inside the class by using the special variable $this in PHP.
The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs [to]). From PHP Manual
Private property example:
class hello {
private $c; # properties defined like this have the value NULL by default
public function b() {
$this->c = 20; # assign the value 20 to private property $c
}
public function output() {
echo $this->c; # access private property $c
}
}
$new = new hello;
$new->output(); # NULL
$new->b();
$new->output(); # 20
Hope this is helpful. You use a private property because everything else in your program does not need to care about it, so inside your class you know that nothing else can manipulate the value. See as well VisibilityDocs.
Every variable of the class is public when you define it inside of a method (function)! You can do this the following way:
class hello {
public function b() {
$this->c = 20;
}
public function output() {
echo $this->c;
}
}
$new = new hello;
$new->output();
or let function b() return $c and then pass it as a variable to output():
class hello {
public function b() {
return $c = 20;
}
public function output($c) {
echo $c;
}
}
$new = new hello;
$c = $new->b();
$new->output($c);
Remember all variables inside a function are ONLY accessable from within that particular function...unless you use $this of course, which makes the variable a class property!
Also, it's recommended to only return variables...echo is only for the real output, the pure HTML, the template, your view, if you know what I mean :)
Try this instead:
class hello {
public $c = null;
public function b() {
$this->c = 20; //I'd like to make $c usable by the method output()
}
public function output() {
echo $this->c;
}
}
class hello {
public $c;
public function b() {
$this->c = 20;
}
public function output() {
$this->c;
}
}
Note: If you need to use a property in another method you don't need to declare that method as public, you should declare the property as private, and you'll have access to your property with no problem:
class hello {
private $c;
public function b() {
$this->c = 20;
}
public function output() {
$this->c;
}
}
class hello {
public $c;
public function b() {
$this->c = 20; //I'd like to make $c usable by the method output()
}
public function output() {
return $this->c;
}
}
$new = new hello;
echo $new->output();
class hello {
var $c;
function __construct() { // __construct is called when creating a new instance
$this->c = 20;
}
public function getC() {
return $this->c;
// beter to return a value this way you can later decide what to do with the value without modifying the class
}
}
$new = new hello; // create new instance and thus call __construct().
echo $new->getC(); // echo the value
Rewrite this code like this :-
class hello {
public $c;
public function b() {
public $this->c = 20; //I'd like to make $c usable by the method output()
}
public function output() {
echo $this->c;
}
}
$new = new hello;
$new->output();