Better way to initialize a class variable - php

I have been searching for a while but I could not find the answer, what are the differences between these two ways of inicialize a variable class in PHP?:(if there are)
class MyClass
{
private $myVariable='something';
public function __construct()
{
}
}
class MyClass
{
private $myVariable;
public function __construct()
{
$this->myVariable='something';
}
}

If you want to initialize the variable with a default value inside the class, choose method 1.
If you want to initialize the variable with a outside value, pass the variable through the constructor and choose method 2.

See this scenario:
class Parent {
protected $property1; // Not set
protected $property2 = '2'; // Set to 2
public function __construct(){
$this->property1 = '1'; // Set to 1
}
} // class Parent;
class Child extends Parent {
public function __construct(){
// Child CHOOSES to call parent constructor
parent::__construct(); // optional call (what if skipped)
// but if he does not, ->property1 remains unset!
}
} // class Child;
This is a difference between the two calls. parent::__construct() is optional for child classes that inherit from a parent. So:
if you have scalar (as in is_scalar()) properties that need to be preset, do it in the class definition to be sure that they exist in child classes too.
if you properties that depend on arguments or are optional, put them in the constructor.
It all depends on how you design your code's functionality.
There's no right of wrong here, it's only what's right for you.

I like to do it somewhat like the second way to facilitate lazy-loading. Simple values I will set when declaring the member variable.
class WidgetCategory
{
/** #var array */
private $items;
/**
*
* #return array
*/
public function getItems()
{
if (is_null($this->items)) {
$this->items = array();
/* build item array */
}
return $this->items;
}
}

You can only use constant values if you don't choose to initialize the variable in the constructor. Here a little example:
define('MY_CONSTANT', 'value');
class MyClass
{
// these will work
private $myVariable = 'constant value';
private $constant = MY_CONSTANT;
private $array = Array('value1', 'value2');
// the following won't work
private $myOtherVariable = new stdClass();
private $number = 1 + 2;
private $static_method = self::someStaticMethod();
public function __construct($param1 = '')
{
$this->myVariable = $param1;
// in here you're not limited
$this->myOtherVariable = new stdClass();
$this->number = 1 + 2;
$this->static_method = self::someStaticMethod();
}
}
Take a look at this Manual Page to see what values are allowed to be directly assinged to properties: http://php.net/manual/en/language.oop5.properties.php
There may be more differences ...

Related

Why is not possible for a child class to inherit properties class that are set dynamically?

Inheritance of properties is possible when a property is hardcoded. See below:
class ParentObj {
protected $familyName = 'Lincoln';
}
class ChildObj extends ParentObj {
public function __construct() {
var_dump($this->familyName);
}
}
$childObj = new ChildObj();
// OUTPUT
string 'Lincoln'
Inheritance of properties is not possible when a property is dynamic. See below:
class ParentObj {
protected $familyName;
public function setFamilyName($familyName){
$this->familyName = $familyName;
}
}
class ChildObj extends ParentObj {
public function __construct() {
var_dump($this->familyName);
}
}
$familyName = 'Lincoln';
$parentObj = new ParentObj();
$parentObj->setFamilyName($familyName);
$childObj = new ChildObj();
// OUTPUT
null
So the question is: Why is not possible for a child class to inherit properties class that are set dynamically?
The child inherits it's initial state from the parent class. It does not inherit from the concrete parent object instance.
In your first example, "Lincoln" is applicable to all ParentObject instances created. In your second example, it is applicable to the concrete $parentObj only. You are setting it specifically to that instance.
See my answer What is a class in PHP? for a more thorough explanation.
If you wanted to access the value of $familyName from all instances(objects), you can define $familyName as static, i.e. create a global class variable.
e.g.
<?php
class ParentObj {
protected static $familyName;
public function setFamilyName($familyName){
self::$familyName = $familyName;
}
}
class ChildObj extends ParentObj {
public function __construct() {
var_dump(self::$familyName);
}
}
$familyName = 'Lincoln';
$parentObj = new ParentObj();
$parentObj->setFamilyName($familyName);
$childObj = new ChildObj(); // Output: Lincoln
$familyName = 'Lee';
$parentObj->setFamilyName($familyName);
$childObj = new ChildObj(); // Output: Lee
Note of Caution: $familyName is now a global and will change for all instances of this object. This could lead to unexpected results if you ever change the value within the script. Global variables are generally considered a Bad Idea.

Why isn't this function working?

I have declared the beats function and am trying to use it. What is the difference between var_dump(is_a($sci, 'Scissors')) and var_dump($roc->beats($sci))? When I run this code the first var_dump returns true and the second false. I want both to return true.
abstract Class Option
{
private $beats;
public function beats($opponentsChoice)
{
return is_a($opponentsChoice, $this->beats);
}
}
Class Rock extends Option
{
private $beats = 'Scissors';
}
Class Paper extends Option
{
private $beats = 'Rock';
}
Class Scissors extends Option
{
private $beats = 'Paper';
}
$roc = new Rock;
$pap = new Paper;
$sci = new Scissors;
var_dump(is_a($sci, 'Scissors'));
var_dump($roc->beats($sci));
The problem is your private variables.
private variables are scoped only to the class they are defined in. Option::$beats is a different variable than Rock::$beats.
If you change your variables to be protected instead, that will work, since all members of a class hierarchy will share protected variables (and methods).

Changing object right before PHP serialises it

I have the following class tree:
class A /* Base class */
{
private/protected/public $state
}
class B extends A /* Auto generated class, not to be modified */
{
private $v
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
There is only one class A. There are multiple classes like class B, and all of those classes will have a subclass like C. Class B gets auto-generated and should not be modified.
I am storing objects of type(s) C in the session. What I want to do is to store some state information in every instance, just before PHP gets it serialised, and that will do something with it when it's unserialised. I want all this to be implemented in class A.
Considering, I need to use either __sleep() or Serializable interface. Using __sleep is out of the question, because of what the PHP manual says:
It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.
Meaning that if I sleep an instance of class C, I'll loose the private variables declared in B. So I want to use Serializable, but for some reason, I simply can't get it to do what I want.
In essence, I would like the object to be serialised just as if I didn't implement any serialisation stuff myself, I just want to add information to $state right before it happens. I've tried covering all data with ReflectionObject->getProperties(), but I can't seem to find the right way to fetch and set the private values in class B to be serialised and unserialised.
How do I do this?
You can do this using the Reflection classes. You'll have to get the properties of the class itself and each of it's parent classes. Getting and setting the property values can be done using ReflectionProperty's getValue and setValue methods, combined with setAccessible to get access to private and protected properties. Combining those, I came up with the following code:
<?php
class A implements Serializable /* Base class */
{
protected $state;
public function serialize()
{
$this->state = "something";
return serialize($this->_getState());
}
public function unserialize($data)
{
$this->_setState(unserialize($data));
}
protected function _getState()
{
$reflClass = new ReflectionClass(get_class($this));
$values = array();
while ($reflClass != null)
{
foreach ($reflClass->getProperties() as $property)
{
if ($property->getDeclaringClass() == $reflClass)
{
$property->setAccessible(true);
$values[] = array($reflClass->getName(), $property->getName(), $property->getValue($this));
}
}
$reflClass = $reflClass->getParentClass();
}
return $values;
}
protected function _setState($values)
{
foreach ($values as $_)
{
list($className, $propertyName, $propertyValue) = $_;
$property = new ReflectionProperty($className, $propertyName);
$property->setAccessible(true);
$property->setValue($this, $propertyValue);
}
}
}
class B extends A /* Auto generated class, not to be modified */
{
private $v;
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
$instance = new C();
$instance->setV("value");
$s = serialize($instance);
$instance2 = unserialize($s);
var_dump($instance, $instance2);
Which seems to do what you want.

How do I use objects and access their methods while within an object in PHP?

How do I use an object (along with its methods and properties) when I'm inside an object?
Say I have useless classes like these:
class Fruit {
private $name; // Name of the fruit.
private $health = 10; // 0 is eaten, 10 is uneaten.
private $object; // This is a PHP object.
public function __construct($name) {
$this->name = $name;
}
public function set($varname,$value) {
$this->$varname = $value;
}
}
class Eater {
private $name;
public function eat($object) {
$object->set('health',0); // I know I can pass and modify objects like this.
// The object is passed by reference in PHP5 (but not 4), right?
}
}
And I use it as such:
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->eat($apple);
?>
But if I modify the Eater class like so:
class Eater {
private $name;
private $objectToEat; // Let's say if I need the object to be over here instead of in a method.
public function set($varname,$value) {
$this->$varname = $value;
}
public function eat() {
$this->objectToEat->set('health',0); // This doesn't work!
}
}
And set the main program like so:
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->set('objectToEat',$apple);
$paul->eat();
?>
How can I access the object's properties from inside a method? I know I use $this->objectToEat to tell PHP I'm talking about the class properity, but since that property is an object, how do I access the object's methods?
I've tried $this->objectToEat->set('health',0) but that doesn't work. I hope you guys understand what I'm trying to get at (sorry, I can't figure out how to condense my question without compromising clarity)!
You have to set the property correctly. Since it's private, you can't do this from outside the object, so you have to use encapsulation:
class Eaters {
private $name;
private $objectToEat;
public function eat() {
$this->objectToEat->set('health',0); // Assumed "object" was just a typo
}
public function setObjectToEat($object) {
$this->objectToEat = $object;
}
}
Then use it like so:
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->setObjectToEat($apple);
$paul->eat();
?>
Note: In this brief example, your original method is a better design. In certain cases, you might want to prime the method to be used by setting properties beforehand, but more often you want to call it with parameters directly, since it's more clear and more reusable (compartmentalized).
This answer modifies Renesis' answer
In the class, the object to eat is a private variable hence you can't go
$paul->objectToEat = $apple;
What you can do is to make a setter method inside Eaters
class Eaters {
private $name;
private $objectToEat;
public function eat() {
$this->objectToEat->set('health',0); // Assumed "object" was just a typo
}
public function setFood($object) {
$this->objectToEat = $object;
}
}
Therefore, you can call the setFood() method instead.
OR
Change eat() to
public function eat($object) {
$this->object->set('health',0);
return $object;
}
Saving the modified object back to the original variable.
OR
class Eaters {
private $name;
public function eat(&$object) { // this passes object by reference
$object->set('health', 0);
}
}
Although this code is not tested, that is how you can pass a variable by reference.
NOTE: You only need the & when defining the method not when you're passing an argument. For more info about Passing by Reference go to this link
It's probably because your eat method isn't accepting any parameters, and the Eaters class has no $object property.
Can you make $objectToEat a reference and then use it as such in the eat() function?
you have to set $this->object in class Eaters
function __construct($object){
$this->object = $object;
}
or
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->eat($apple);
?>
class Tester {
private $variable;
private $anObj;
public function testFn($val) {
$this->variable = $val;
$this->anObj = new SecondObj();
$this->doSomething();
}
public function doSomething() {
echo("My variable is set to " . $this->variable);
$this->anObj->wow();
}
}
class SecondObj {
public function __construct() {
echo("I'm new!");
}
public function wow() { echo("Wow!"); }
}
$tester = new Tester();
$tester->testFn(42);
Output:
I'm new!My variable is set to 42Wow!

PHP - How do I set the value of a multidimensional array property from 'outside' the class?

I'm trying to wrap my head around something that I can't seem to figure out a solution for. In class A I have a property which is a multidimensional array. In class B which is instantiated within class A, I'd like to set an index value for the property from class A. I run into a wall where I have no idea how to iterate through the property to set the value for the index I'd need to. Here's a test example of what I mean:
<?php
class SomeClass
{
protected $class;
protected $book;
public function __construct()
{
$this->book["genre"]["title"] = "The Haunting";
}
public function SomeTest()
{
$this->class = new AnotherClass();
$this->class->AnotherTest();
}
}
class AnotherClass
{
protected function setBook()
{
$indexes = func_get_args();
$value = array_pop($indexes);
/**
* So now I have the indexes which lead to the array
* depth I'd like to set a value to, but where do I
* go from here?
*
*/
}
public function AnotherTest()
{
$this->setBook("something", "else", "1984");
}
}
$someclass = new SomeClass();
$someclass->SomeTest();
?>
I'm so lost on how to do what I'm thinking of.
initiate class Another with a parameter, pass initiator class!
class SomeClass{
public function SomeTest() {
$this->class = new AnotherClass($this);
$this->class->AnotherTest();
}
}
I use creator for these situations!
class AnotherClass{
function __contruct($creator){
$this->creator=$creator
}
function AnotherTest(){
$this->creator->setBook("something", "else", "1984");
}
In such a situation why you are not using inheritance? Your property is protected and I think it should not be accessible outside the class.
Hay you tried with inheritance?.
Thanks.

Categories