I've learned that OOP is all about data encapsulation, but what about passing data between classes that have nothing to do with each other (would the below example be worthy of using extends)?
class Dog {
private $secretVar;
public function getSecretVar() {
$this->secretVar = 'psst... only for rainbow!';
return $this->secretVar;
}
}
class Rainbow {
public function __construct(Dog $Dog) {
print_r($Dog->getSecretVar());
}
}
$Dog = new Dog();
$Rainbow = new Rainbow($Dog);
// ... classes that don't need the $secretVar
How would you encapsulate $secretVar for only classes Dog and Rainbow? As of now, anyone can call getSecretVar(), and I'm having a hard time allowing that to happen as it seems to defeat the whole point of encapsulation.
Here's a solution, although, it's ugly.
class Dog {
private $secretVar = 'psst... only for rainbow!';
public function getSecretVar($caller == NULL) {
// Here's the trick...
if (get_class($caller) == 'Rainbow') {
return $this->secretVar;
} else {
return '';
}
}
}
class Rainbow {
public function __construct(Dog $Dog) {
print_r($Dog->getSecretVar($this));
}
}
$Dog = new Dog();
$Rainbow = new Rainbow($Dog);
// ... classes that don't need the $secretVar
It's ugly because it hard to maintain and not intuitive. If you really need to do this, there's most likely a flaw in your design.
It wouldn't make sense for a Dog to extend Rainbow or vice versa just to share a variable.
What you are asking of may be possible but I don't know. If it was C++ using the friend visibility, it is certainly possible.
In this case, you have to make it public or use a getter and setter.
Encapsulation is not ment to hide the value of the variable from the rest of the program but to have full control of how the rest of your program can access the variable.
By declaring the variable private you can check what values it can be set to and you can make changes to it before anybody reads it.
There is no real point in trying to let only some of the classes read the variable.
What you are trying to do could be achieved by using reflection to check which class and method calls the getSecretVar() method, but it's hardly ever useful.
In your case, you could use protected like this: (every class that extends hasSecret will have access to it.)
<?php
class HasSecret {
protected $secretVar = 'psst... only for rainbow!';
}
class Dog extends HasSecret {
public function getSecretVar() {
return $this->secretVar;
}
}
class Rainbow extends HasSecret {
public function __construct(Dog $Dog) {
print_r($Dog->getSecretVar());
}
}
$Dog = new Dog();
$Rainbow = new Rainbow($Dog);
Related
Likely this has already been asked, but nevertheless, here goes. This may fall under best practice or security... I'm not really sure.
In my application, I am using a nested object, that is called in the __construct() function. Sort of like this:
class user {
public $userID = NULL;
public $someObject = NULL;
public function __construct() {
$this->userID = getThisUser();
$this->someObject = new objectBuilder($this->userID);
}
public function getThisUser() {
// ...
}
}
class objectBuilder {
public $buriedVar = NULL;
public function __construct($uid = NULL) {
if( !isset($uid) ) {
$this->buriedVar = setTheObject($uid);
} else {
$this->buriedVar = setTheObject(0);
}
}
public function setTheObject($id) {
// ...
return "random string";
}
}
$tom = new user();
Obviously terrible outline here, but the point is, I can then call $tom->someObject->buriedVar and it'll return "random string".
While looking for a way to nest classes, I noticed no one recommends this as a method for storing objects inside of another object. I'm curious of a few things:
1) Is this insecure?
2) Are the vars inside the nested object exclusive to the call made inside $tom->__construct(), or if I create another object using new objectBuilder() is it overwriting the one inside $tom->someObject? I haven't noticed this, but am not sure how to test for that entirely.
3) Is there something else I'm missing? A best practice reason not to instantiate an object inside a class? I've been using it for years and it works great for what I've done. Is it a speed thing?
1) Is this insecure?
Not inherently, no.
2) Are the vars inside the nested object exclusive to the call made
inside $tom->__construct(), or if I create another object using new
objectBuilder() is it overwriting the one inside $tom->someObject? I
haven't noticed this, but am not sure how to test for that entirely.
This is a fundamental question between class and object. Objects are instances of a class and there can be multiple. The only things that would be overwritten are static properties and methods. You could test it like this:
<?php
$obj1 = new objectBuilder();
$obj2 = new objectBuilder();
if ($obj1 !== $obj2) {
echo "objects are not the same\n";
}
if ($obj1->buriedVar !== $obj2->buriedVar) {
echo "nested objects are not the same either\n";
}
$obj3 = new objectBuilder(1);
if ($obj1->buriedVar != $obj3->buriedVar) {
echo "even the values of two different buried vars with different values are different.\n";
}
if ($obj1->buriedVar == $obj2->buriedVar) {
echo "counter-example: nested variables with the same values set are similar.\n";
}
It helps to know the difference between equality and identity (see this SO post).
3) Is there something else I'm missing? A best practice reason not to
instantiate an object inside a class? I've been using it for years and
it works great for what I've done. Is it a speed thing?
You touched on it briefly. What you should know is that this is not scalable and is difficult to test.
Imagine you're creating a website for dogs.
<?php
class Bio
{
public function __construct()
{
$this->dog = new Dog('Terrier');
}
}
class Dog
{
private $animal = 'dog';
private $noise = 'woof!';
private $breed;
public function __construct($breed=null)
{
$this->setBreed($breed);
}
public function setBreed($breed)
{
$this->breed = $breed;
}
}
What if you want to add a new breed? Well... That's easy enough:
class Bio
{
// ...
public function __construct($breed)
{
$this->dog = new Dog($breed);
}
// ...
}
Cool! You've solved everything.
Except...
One day you want to create a section for cats, because one of your best writers also loves cats, and you sense an untapped market.
Uh oh...
You can refactor the code, of course. But you wrote it a long time ago. Now you have to go in and figure out where everything went. No big deal.. A bit annoying but you fixed it!
But now you have another problem. Turns out that the same author wants to add different traits to the breed. You're surprised this hasn't come up sooner but, hey, it's probably a good thing to have.
Now you need to go in to the Dog object, and the Cat object, and add traits.
Every single time.
On. Every. Bio.
After some reconfiguring, you've created something monstrous like this:
$article1 = new Bio('Terrier', 'dog', ['independent']);
$article2 = new Bio('Persian', 'cat', ['flat-faced']);
//... and so on, and so on
The next time the author asks for something, you fire her and then tear your hair out in a mad rage.
Or, from the beginning, you use Dependency Injection.
<?php
class Bio
{
private $animal;
public function __construct(AnimalInterface $animal)
{
$this->animal = $animal;
}
}
interface Animal
{
public function getType();
public function setBreed($breed);
public function getBreed();
public function setTraits(array $traits);
public function getTraits();
}
abstract class AbstractAnimal implements AnimalInterface
{
private $breed;
private $traits = [];
abstract public function getType();
public function setBreed($breed)
{
$this->breed = $breed;
}
public function getBreed()
{
return $this->breed;
}
public function setTraits(array $traits)
{
$this->traits = $traits;
}
public function getTraits()
{
return (array)$this->traits;
}
}
class Cat extends AbstractAnimal
{
public function getType()
{
return 'cat';
}
}
class Dog extends AbstractAnimal
{
public function getType()
{
return 'dog';
}
}
This pattern requires little to no editing after it has been created.
Why? Because you are injecting the object to nest into the class, rather than instantiating it in the object.
$bio1 = new Bio($dog); $bio2 = new Bio($cat); can always stay like this. Now you just edit the $dog and $cat objects. The added benefit is that these objects can be used anywhere.
But what about utility classes?
(This is where testability comes in. If you haven't worked with unit testing, I recommend reading up on it in the link to PHPUnit below. I'm not going to dwell on how that works as it's off topic).
Dependency Injection is well and good if you have classes that require customization. But what about utility classes that just house various functions?
class Utils
{
public function add($a, $b)
{
return $a + $b;
}
}
You might think that you can call this function safely from the constructor. And you can. However, one day you might create a log method in your Utils class:
public function log($msg)
{
exec("cat '$msg' > /tmp/log.txt");
}
This works just fine. However, when you run tests, your /tmp/log.txt file complains. "Invalid permissions!". When this method is run via your website, log.txt needs to be writeable by www-data.
You could just chmod 777 /tmp/log.txt, but that would mean everyone who has access to your server can write to that log. Additionally, you may not want to always write to the same log when you're testing as when you're navigating through the web interface (Personally, I would find it confusing and cluttering).
PHPUnit and other unit testing services allow you to mock various objects. The problem is that you have classes calling Utils directly.
You have to find a way to manually override the constructor. Look at PHPUnit's manual to find out why this maybe isn't ideal.
So if you're not using Dependency Injection, what do you do?
PHPUnit suggests, amongst other fixes, moving this Utils object instantiation to another method and then stubbing/mocking that method in your unit test (I want to emphasize that this is after recommending Dependency Injection).
So the next best?
public function __construct()
{
$this->init();
}
private function init()
{
$this->utils = new Utils;
}
Now when you unit test, you can create a fake init method and it will be called as soon as the class is created.
In conclusion, the way you are currently instantiating classes is not scalable or easily testable in many real world situations. While it may be all right in limited situations, it is better to get used to the DI (Dependency Injection) pattern, because it will save you lots of headaches in the future.
I've been trying for a long time now to find a correct design using PHP to achieve what I want, but everything I've tried failed and I'm guessing it's probably because I'm not looking from the right angle, so I wish some of you can enlighten me and give me some good advice!
The design might seem a little weird at first, but I assure you it's not because I like to make things complicated. For the sake of simplicity I'm only giving the minimal structure of my problem and not the actual code. It starts with these:
<?php
// ------------------------------------------------------------------------
class Mother_A
{
const _override_1 = 'default';
protected static $_override_2 = array();
public static function method_a()
{
$c = get_called_class();
// Uses $c::_override_1 and $c::$_override_2
}
}
// ------------------------------------------------------------------------
class Mother_B extends Mother_A
{
public function method_b()
{
// Uses self::method_a()
}
}
Class Mother_A defines a static method that uses constants and statics to be overridden by children. This allows to define a generic method (equivalent of a "template" method) in the derived class Mother_B. Neither Mother_A or Mother_B are intended to be instanciated, but Mother_B should not be abstract. This exploits Late Static Binding, which I find very useful btw.
Now comes my problem. I want to define two classes, in n distinct 'situations' (situation 1, situation 2, etc):
<?php
// ------------------------------------------------------------------------
class Child_A_Situation_k extends Mother_A
{
// Uses method_a
}
// ------------------------------------------------------------------------
class Child_B_Situation_k extends Mother_B
{
// Uses method_a and method_b
}
Of course I'm not actually giving these stupid names; both classes have different names in each situation, but both follow the same derivation pattern from Mother_A and Mother_B. However, in each individual case ('situation'), both classes need the exact same constants/static override, and I don't know how to do that without duplicating the override manually in both classes.
I tried many things, but the closest I got was to implement an interface Interface_Situation_k that defined constants and statics for the situation k, and make both children implement this interface. Of course, you can't define statics in an interface, so it failed, but you get the idea. I would have traded the interface for a class, but then there's no multiple inheritance in PHP, so it's not valid either. :/ I'm really stuck, and I can't wait to read a possible solution! Thanks in advance!
this is the best i can do, i don't think there is a way to do it with less code.
Look at the comments inside the code for more info.
Fully working code:
<?php
class Mother_A
{
// you're using '_override_1' as a variable, so its obviously not a constant
// also i made it public for the setSituation function,
// you could keep it protected and use reflections to set it
// but i dont really see a reason for that.
// if you want that, look up how to set private/protected variables
public static $_override_1 = 'default';
public static $_override_2 = array();
public static function method_a()
{
$c = get_called_class();
var_dump($c::$_override_1);
var_dump($c::$_override_2);
// Uses $c::_override_1 and $c::$_override_2
}
public static function setSituation($className)
{
$c = get_called_class();
// iterate through the static properties of $className and $c
// and when the you find properties with the same name, set them
$rBase = new ReflectionClass($c);
$rSituation = new ReflectionClass($className);
$staBase = $rBase->getStaticProperties();
$staSituation = $rSituation->getStaticProperties();
foreach($staSituation as $name => $value)
{
if(isset($staBase[$name])) $c::$$name = $value;
}
}
}
// ------------------------------------------------------------------------
class Mother_B extends Mother_A
{
public function method_b()
{
self::method_a();
}
}
class Situation_k
{
public static $_override_1 = 'k';
public static $_override_2 = array('k','k');
}
class Child_A_Situation_k extends Mother_A { }
Child_A_Situation_k::setSituation('Situation_k');
// This is not as short as writing 'extends Mother_A, Situation_k'
// but i think you wont get it shorter
class Child_B_Situation_k extends Mother_B { }
Child_B_Situation_k::setSituation('Situation_k');
echo '<pre>';
Child_A_Situation_k::method_a();
echo "\n";
Child_B_Situation_k::method_a();
echo "\n";
Child_B_Situation_k::method_b();
echo "\n";
echo '</pre>';
?>
Ok, i know that probably this is not possible, but i'll give it a try. Maybe my knowledge is not so good. Please don't downvote, this is just for curiosity and this maybe due to a poor OO design.
Assume that class definition for A is:
class A
{
public function build()
{
$data = $this->fetchData();
...
}
public function fetchData()
{
// Database eavy query
return $this->repository->fetchDataForCharting();
}
}
And then, without using a good and decent OO design, you create class B which is going to use fetchDataForCharting from the repository, for doing some other different calculations:
class B
{
public $data;
public function getMonthlyLimit()
{
// Database eavy query
$data = $this->getData();
}
public function getData()
{
return isset($this->data) ? $this->data :
$this->repository->fetchDataForCharting();
}
}
Sometimes you need only A, sometimes you need only B, sometimes both. Is there any way of overriding fetchData at runtime? I mean someting like $.extend() in jQuery:
$b = new B();
$b->getMonthlyLimit(); // $b->data filled
$a = new A();
// Override fetchData
$a = extend($a, 'fetchData', function() use($b) { return $b->data; });
$a->build(); // Just 1 query
There is no proper way to modify/replace/add a class method.
The runkit extension has runkit_method_redefine() and runkit_method_add() but both are experimental and ugly (they take a code string, not a Closure object like your anonymous function is).
It is not really clear to me what you are trying to do, but in PHP you can have calss B extend class A by writing the class B declaration as so:
Class B extends Class A {
...
}
You can the override the fetchData function within Class B like
public function fetchData {
// do something different than in class A
}
Or
public function fetchData {
// execute the Class A functionality first
parent::fetchData();
// now do something in addition to what is in Class A functionality
}
Hope this helps.
The short answer is no.
The closest you can get to exactly what you are asking is something like:
class A
{
public function __construct()
{
$this->bar = function()
{
};
}
public function foo()
{
$bar = $this->bar;
$bar();
}
}
$a = new A();
$a->bar = function()
{
// override
};
$a->foo();
Requires PHP 5.4 for $this support. But keep in mind that people do that sort of thing in JavaScript because it doesn't have "real" object oriented programming. You shouldn't try to emulate JavaScript, Ruby, Python, etc, just for the sake of doing it.
To be clear: in PHP, inheritance or composition or dependency injection is probably the proper way to go about what you are trying to do. (The above hack is definitely not what you should be doing.)
I'm sorry I could not come up with a more descriptive title. My problem is the following. Assume that you have two classes A and B and you know that sometimes it may happen that some code tries to instantiate an object of type A when what is actually needed is an object of type B. The point is that the code to decide which one is the right object naturally belongs to class A and not to the client code.
In javascript (ok, js doesn't have classes, but it makes the point clear) you can simply do
function A() {
if(some_condition) {
return new B();
}
//else we proceed to customize and return our object
}
I want to do something similar in PHP. The best thing I can come up with is
class A {
private function __construct() {
//whatever you need to do
}
public static function getInstance() {
if(some_condition) {
return new B();
}
else {
return new A();
}
}
}
The problem is that the client code will always have to know that A is special and you have to instantiate objects with a static method.
Is there a way to delegate to A the choice of the type of object to return in a seamless way?
Unfortunately no, the best i think you can do is something like:
class Decider {
public static function decide() {
if(some_condition) {
return "A";
}
else {
return "B";
}
}
}
$new_class = Decider::decide();
$new_object = $new_class();
But then again this is really no different then the way you approached it. I wouldnt consider this an invalid design patter though, however i would leave it to an external class to do the deciding rather then have class "A" or class "B" do the deciding within them. Ideally your classes should be encapsulated in such a way that they do not require other classes unless they are member variables of the class itself or passed into the class for functional purposes.
Here's the objective. I have a PHP class and there are one or two of its methods that I would like to override with my own. As I understand OOP (in PHP and in general) I could write a child class that extends it and overrides the functionality of the methods in question.
However, I was wondering if this is the best way of achieving this task and if this is a proper use for child classes or if there is something better in PHP for what I'm trying to do.
Yes, that is the best way to do it. The idea of extending a class is to provide extra or more specific functionality.
yes. this exactly what you use inheritance for.
A good principle though, is make sure your new inheriting class "is-a" base class.
Like Human is-a Mammal. Don't do Alien extends Human just because Alien does a lot of stuff Humans do.
So, are you asking if overriding methods (which requires extending the superclass) is the best way to override methods? Yes.
That is the most important use of subclasses. In fact, polymorphism is at the heart of OOP. The other use is to provide new properties/methods, but that usually won't be enough.
class oldClass {
public function methodToOverride() {
echo 'oh hai';
}
}
class childClass extends oldClass {
public function methodToOverride($arg, $arg2) {
// your custom code
echo 'hai world ' . $arg . ' ' . $arg2;
// if you need to still call the parent class
parent::methodToOverride();
}
}
$child = new childClass();
$child->methodToOverride('nom', 'nom');
Sounds like you're on the right track.
For the methods you want to override, you'd probably want to:
make sure you're not changing the intent of the method
know whether you have to call the base class's method within, and when
The only other way, would be to create a generic "superclass"...
class SuperClass {
protected $obj = null;
protected $overrides = array();
public function __construct($obj) {
if (!is_object($obj)) {
throw new InvalidArgumentException('Argument is not an object');
}
$this->obj = $obj;
}
public function __call($method, $args) {
$method = strtolower($method);
if (isset($this->overrides[$method])) {
array_unshift($args, $this);
return call_user_func_array($this->overrides[$method], $args);
} elseif (is_callable(array($this->obj, $method))) {
return call_user_func_array(array($this->obj, $method), $args);
} else {
throw new BadMethodCallException('Invalid Method Called');
}
}
public function __get($var) {
return isset($this->obj->$var) ? $this->obj->$var : null;
}
public function __set($var, $value) {
$this->obj->$var = $value;
}
public function addOverride($method, $callback) {
$this->overrides[strtolower($method)] = $callback;
}
}
It's not always the best solution, but it's possible that some situations exist to use something like that. It will let you "add" and "override" methods to any object at run time.
The better generic solution is to simply extend the class in a child class... But the above "superclass" does have some uses...
Another route I haven't seen mentioned here is to question whether it wouldn't be better to extract an abstract class and have the two classes extend that. Of course you would need to be able to alter the code of the first class for that (e.i. if the class came from an open source library you might refrain from changing the code).
you are on the right track