Jump to content

Recommended Posts

I have a pretty large project now that uses quite a couple of classes. Now I've stumbled upon this problem.

 

I have a class with properties that are public. Objects of this class are inserted into a container (which is another class).

I have made so that if any of the properties are set to false (in the __set function), then the value must be reverted to the default value.

 

The default value is taken from an object that is created in the construct (object of oneself witjin object).

I think this causes an infinite recursion. What's the best way to solve this problem?

 

This container may contain like 10 objects of my class, each of these in turn - an object with default values that is same for all. It seems unnecessary to create this object in every object.

 

I know this sounds a bit confusing but don't know how to solve this.

 

Also, if the container class is the one to have the default values then the object class needs to have access to it somehow, but even if that would be possible then each object would be really huge because it also contains the container??

 

class SmallObject() {
   private $default_object;

  function __construct() {
     $this->color = '#5432FA';
     $this->size = 5.3;

     $this->default_object = new SmallObject();
  }

  function __set($property, $value) {
     if ($value === false)
        $this->$property = $this->default_object->$property;
  }
}

Link to comment
https://forums.phpfreaks.com/topic/268874-creating-default-object-within-object/
Share on other sites

The default value is taken from an object that is created in the construct (object of oneself witjin object).

I think this causes an infinite recursion. What's the best way to solve this problem?

 

Obviously by not creating the object inside the constructor...

 

class SmallObject {
 private $default_object;
 protected function getInner() {
   if ($this->default_object === null) {
     $this->default_object = new static();
   }
   return $this->default_object;
 }
 public function __set($key, $val) {
   if ($val === false) {
      $val = $this->getInner();
   }
   $this->{$key} = $val;
 }
}

 

This container may contain like 10 objects of my class, each of these in turn - an object with default values that is same for all. It seems unnecessary to create this object in every object.

 

It does. Simply make it static. Static means it is shared across all instances.

 

class SmallObject {
 private static $default_object;
 protected function getInner() {
   if (static::$default_object === null) {
     static::$default_object = new static();
   }
   return static::$default_object;
 }
 public function __set($key, $val) {
   if ($val === false) {
      $val = $this->getInner();
   }
   $this->{$key} = $val;
 }
}

 

Also, if the container class is the one to have the default values then the object class needs to have access to it somehow, but even if that would be possible then each object would be really huge because it also contains the container??

 

Get out of that mindset of thinking that your consuming too much memory and that you should limit the number of objects you create. Pre-mature optimization is evil! First make it work, then make it work fast!

Edited by ignace

Your use of objects is wrong. Member variables can contain default values simply by setting them when the are declared. A class should not hold an instance of itself unless you are using the SIngleton pattern. This is where you would not want the object to accidentally be redeclared and its values reset i.e a database object.

 

A second object can hold instances of the first object and use its methods. See below

 

<?php
// a simple class
class foo
{
public $color = '#5432FA';
public $size = '5.3';

public function __construct($color = FALSE, $size = FALSE)
{
if($color)
{
$this->color = $color;
}
if($size)
{
$this->size = $size;
}
}

public function get_color()
{
return $this->color;
}

public function get_size()
{
return $this->size;
}
}

// another simple class that contains foo objects
class bar
{
protected $foos = array();

public function add_foo(foo $foo)
{
$this->foos[] = $foo;
}

public function display_foo_data()
{
foreach($this->foos as $foo)
{
print "<p>Color: ".$foo->get_color().", Size: ".$foo->get_size()."</p>";
}
}
}


// usage
$a = new foo();
$b = new foo('#FFFFFF', 10);
$c = new foo();

$bar = new bar();
$bar->add_foo($a);
$bar->add_foo($B);
$bar->add_foo($c);
$bar->display_foo_data();
?>

Edited by neil.johnson

Thanks for the help, ignace. I've got some questions though.

 

1. I had no idea you could use the keyword static as in static:: (I mean, it's not a class?) or static() (it's not a function?). What do these mean?

2. Where in your solution do I give the properties of default_object its default values? Remember that the values of default_object must be the initial values of my SmallObject upon construction.

3. By the way, I use this object so that new objects can inherit properties from an existing object. If I want to set some property to its default value I do like this: $object3->size = false;

4. Why is default_object access type protected? Can it be private?

5. In the __set function, if I forget $this->{$key} = $val;, will the value never be set, i.e. will it override/prevent default behavior?

Edited by silkfire
1. I had no idea you could use the keyword static as in static:: (I mean, it's not a class?) or static() (it's not a function?). What do these mean?

 

See http://php.net/manual/en/language.oop5.late-static-bindings.php

 

4. Why is default_object access type protected? Can it be private?

 

It already is.

 

Like Neil already mentioned you are possibly going about this all wrong.

 

By the way, I use this object so that new objects can inherit properties from an existing object.

 

You can simply clone an existing object.

 

$prototype = new SmallObject(); // contains all defaults

$newSmallObject = clone $prototype;
$newerSmallObject = clone $prototype;

 

If I want to set some property to its default value I do like this: $object3->size = false;

 

Something that also may work is:

 

class SmallObject {
 private $data;
 private $defaults;

 public function __construct() {
   //setup data and defaults
   $this->color = 'foo';
   $this->size  = 'bar';
 }

 public function __set($key, $val) {
   if ($val === false) {
     $val = $this->defaults[$key] ?: null;
   }
   if (!isset($this->defaults[$key])) {
     $this->defaults[$key] = $val;
   }
   $this->data[$key] = $val;
 }

 public function __get($key) {
   return $this->data[$key];
 }
}

 

$smallObject = new SmallObject();
$smallObject->color = 'bat';
echo $smallObject->color, PHP_EOL; // bat
$smallObject->color = false;
echo $smallObject->color, PHP_EOL; // foo

 

Not sure either of these are an ideal solution for you either. Can you explain why you have opted to do it like this? Why do you need to be able to set them back to their default?

Edited by ignace

Thanks again.

 

This line right here doesn't make sense to me: $val = $this->defaults[$key] ?: null;

Will it be null either way?

 

Well I can't explain what exactly I'm working with but I want the object creation to use as few steps as possible. When I create an object with properties and want to create a new one but with different color and not copy its size, I want the class to automatically take care of this for me.

 

$a = new SmallObject();
$a->color = 'red';
$a->size = 32;
$a->shared = true;
$a->url = 'www.example.com';
$a->country = 'DE';
...
// 6-10 more properties

 

Instead of assigning all those properties again if I want to change country and use default color I do:

 

$b = clone $a;

$a->color = false;                  // Set to default value
$a->country = 'US';

 

Or am I making this harder than it should be?

$val = $this->defaults[$key] ?: null;

 

Means that $val === null when $this->defaults[$key] evaluates to false, it's the value of $this->defaults[$key] otherwise.

 

When I create an object with properties and want to create a new one but with different color and not copy its size, I want the class to automatically take care of this for me.

 

You can use a Factory for that but then that only works at object creation time, so afterwards setting a value to false, means it will be false.

 

You can use a Prototype, create an object that holds all the default values and use the clone keyword to create new objects from these defaults.

 

But neither of these make sure that when you set a given property to false (what with boolean properties?) it will get set back to the default value. To do this you need to do something like:

 

class SmallObject {
 private $data = array();
 private static $defaults = array(
   'color' => '..',
   'size' => '..',
 );

 public function __construct($data = array()) {
   $this->data = array_merge(self::$defaults, $data);
 }

 public function __set($key, $val) {
   if ($val === false) {
     $val = self::$defaults[$key];
   }
   $this->data[$key] = $val;
 }

 public function __get($key) {
   return $this->data[$key];
 }
}

 

Every new object will have these defaults set:

 

$o1 = new SmallObject();
echo $o1->color, PHP_EOL; // ..
$o1->color = 'bar';
echo $o1->color, PHP_EOL; // bar
$o1->color = false;
echo $o1->color, PHP_EOL; // ..
$o2 = new SmallObject(array('color' => 'foo'));
echo $o2->color, PHP_EOL; // foo

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.