I'm trying to set a property to a class instance dynamically. I've written the following:
class MyClass{
public function setProp($prop){
$this->{$prop}=$prop;
}
}
$inst= new MyClass();
$vari='123';
$inst->setProp($vari);
echo $inst->vari;
I'm expected that after $inst->setProp($vari); method invokation the $inst MyClass instance has the vari field with the 123 value. But it's not true. How to fix this?
In your case you will set property with name 123 which is not valid property. If your code was
$vari='asd';
$inst->setProp($vari);
echo $inst->asd;
You would successfully create property with name asd and the same value
Because it's getting the variable value, but not its name.
(EDIT: My previous explanation was wrong)
You'd better has a second parameter with the property name.
class MyClass{
public function setProp($name, $value){
$this->{$name}=$value;
}
}
$inst= new MyClass();
$vari='123';
$inst->setProp('vari',$vari);
echo $inst->vari;
Which ofcourse is like an overkill, if you do not have any logic around it (to be a valid setter).
Undefined properties will be set automatically
<?php
class MyClass{
}
$inst= new MyClass();
$inst->vari = 123;
echo $inst->vari;
P.S.: You will get into a trouble if you try it when the property exists and its private/protected
class MyClass{
private $vari = 100;
public function setProp($name, $value) {
$this->{$name} = $value;
}
}
$inst= new MyClass();
$vari = 123;
$inst->setProp('vari', $vari);
echo $inst->vari;
Will result into Fatal error: Cannot access private property MyClass::$vari
To access private/protected/non-existent properties you can use magic method __get(). In order to write into then, you should use __set().
class MyClass{
private $vari = 100;
public function __set($name, $value) {
$this->{$name} = $value;
}
public function __get($name) {
return $this->{$name};
}
}
$inst= new MyClass();
$inst->vari = 123;
echo $inst->vari;
However, if you don't have real reason to expose your inaccessible properties, it's not good idea to do it.
Related
<?php
class a{
public function out(){
$this->test = 8;
return $this->test;
}
}
$b = new a();
echo $b->out();
?>
output: 8
when i run this code, output the result 8 .
but when i add __set() function, it output a notice, and not 8 output
<?php
class a{
public function __set($property, $value) {
}
public function out(){
$this->test = 8;
return $this->test;
}
}
$b = new a();
echo $b->out();
?>
output:
PHP Notice: Undefined property: a::$test in /usercode/file.php on line
13
why is it happening?
As per the docs
__set() is run when writing data to inaccessible properties.
Since you do not have anything in your __set body, the property is not created and therefore not available. You have to define the method body.
class a{
public function __set($property, $value) {
$this->$property = $value;
}
public function out(){
$this->test = 8;
return $this->test;
}
}
$b = new a();
echo $b->out();
Now THAT outputs 8.
Update
You are asking why the first block of code works and the second does not. Take a look at PHP source code here and you will see the explanation in the code itself.
Looks to me, that when you do not have __set() in your class and you do $this->test, PHP internally calls it's own __set(), which does exactly what it does: sets the property name to certain value.
But when you define __set() with empty body, it overrides the default internal __set() and does nothing. And that is the main reason for your code to fail - the requested property has not been set neither by your __set(), nor by the internal one.
When a::out() runs, there is no $test property in the object. This is why $this->test = 8; invokes a::__set().
But a::__set() doesn't create the $test property and the next statement (return $this->test;) cannot find it and produces the notice.
You should declare the object properties in the class definition and initialize them in the constructor (if appropriate):
class a {
private $test; // <-- because of this, $this->test exists...
public function __set($property, $value) {
}
public function out() {
$this->test = 8; // ... and __set() is not invoked here
return $this->test;
}
}
Without __set() being defined, the statement $this->test = 8; creates the $test property of the current object if it is not already created (by its definition or by a previous assignment to it) then stores 8 into it.
When __set() is defined, any attempt to set a property that doesn't exist or it is not accessible (setting inside the class a private property inherited from the parent class or setting a protected or private property outside the class) is handled by __set(). Your implementation of __set() doesn't create the missing property and it basically turns the statement $this->test = 8; into a no-op.
The following is true.
<?php
class a{
public function __set($property, $value) {
$this->$property = $value;
}
public function out(){
$this->test = 8;
return $this->test;
}
}
$b = new a();
echo $b->out();
you should look at is php overloading
Find the answers in the manual.
How can I access to class variable from outside without creating new instance in PHP ? Something like this:
class foo
{
public $bar;
}
echo foo::$bar;
Is it possible or I must create method that will print or return this value and use it or create new instance ( $a = new foo; echo $a->$bar ) ?
EDIT: I don't want to create constant but classic variable that will be changed later.
make this variable static to access it with out class object.
If you wanted to change static variable value by method then you need to use static method
you can try like this:
class foo
{
public static $bar ="google";
public static function changeVal($val){
self::$bar=$val;
}
}
foo::changeVal("changed :)");
echo foo::$bar;
Output : changed :)
Demo : https://eval.in/107138
You also can changed it like this without static method:
foo::$bar = "changed";
demo : https://eval.in/107139
like this:
class foo
{
public static $bar ="google";
}
echo foo::$bar;
Output: google
demo: https://eval.in/107126
IF it makes sense to use a static variable:
class foo{
public static $bar = 'example';
}
Which could be accessed like so:
echo foo::$bar;
If the value will never change during runtime, then you probably want a class constant (http://www.php.net/oop5.constants)
class foo {
const bar = 'abc';
}
...otherwise you want a public static variable (http://www.php.net/manual/en/language.oop5.static.php)
class foo {
public static $bar = 'abc';
}
...either way, access it like this
echo foo::bar;
You can access the class variable without creating instances only when the variable is markesd as static:
class foo
{
public static $bar;
}
This is how you use a class variable:
// with instantiation
class foo {
// initialize on declare
public $bar = 1;
// or initialize in __construct()
public function __construct(){
$this->bar = 1;
}
}
$foo = new foo();
var_dump($foo->bar);
// static way
class static_foo {
public static $bar = 1;
}
var_dump(static_foo::$bar);
And this is how you instantiate a class from a random class name string variable.
$foo = new foo();
$random_class_name = $foo->bar;
try {
// following line throws if class is not found
$rc = new \ReflectionClass($random_class_name);
$obj = $rc->newInstance();
// can be used with dynamic arguments
// $obj = $rc->newInstance(...);
// $obj = $rc->newInstanceArgs(array(...));
} catch(\Exception $Ex){
$obj = null;
}
if($obj){
// you have a dynamic object
}
What's your actual question?
I want to know if I can somehow assign new variable without making constructor.
It seems pretty big overkill to create constructors on every class just to set initial private class variables.
Here is my example of what I want to achieve
<?php
class MyClass {
public function DoSomething() {
echo '1';
}
}
class MySecondClass {
private $obj = new MyClass(); // Error
/*
// This works, but I don't like it, I think it's total overkill
function __construct() {
$this->obj = new MyClass();
}
*/
public function PrintOne() {
$this->obj->DoSomething();
}
}
$class = new MySecondClass();
$class->PrintOne();
Just so it's perfectly clear here's the error message
syntax error, unexpected 'new' (T_NEW) on line 10
You can't (that I know of), you need to either instantiate it in the constructor (Option A), or pass in the object (Option B).
Option A:
class MySecondClass {
private $obj;
function __construct() {
$this->obj = new MyClass();
}
public function PrintOne() {
$this->obj->DoSomething();
}
}
Option B:
class MySecondClass {
private $obj;
function __construct(MyClass $obj) {
$this->obj = $obj;
}
public function PrintOne() {
$this->obj->DoSomething();
}
}
You can't do that in that manner. You can have properties be initialized but they need to be a constant value and not the result of a function or trying to instantiate a class.
http://www.php.net/manual/en/language.oop5.properties.php
IMO, You shouldn't instantiate new objects within the constructor of your classes. You should pass them in as arguments for creating the object.
<?php
class A
{
public $attribute1;
function operation1()
{
echo 'operation1';
}
}
$a = new A();
$a->attribute3 = 10;
echo $a->attribute3;
when i run above script, It shows: 10
Question:
There is no declaration of attribute3 in class A? why i can still use it $a->attribute3 = 10;?
As #Hamish said ... because that is how PHP works.
Just like you can say:
$a = "hello";
and create a property in a function's scope or in the global scope you can use
$obj->a = "hello";
to create a property in the $obj instance's scope.
If this is undesired behavior you can throw an exception using the __get and __set magic methods.
class A{
public $property1;
public function __get($property){
throw new Exception($property." does not exist in ".__CLASS__);
}
public function __set($property, $value){
throw new Exception($property." does not exist in ".__CLASS__);
}
}
In short: because you can.
PHP lets you define object attributes without declaring them in the class.
It's not an uncommon feature, e.g. python:
class Foo(object):
pass
foo = Foo()
foo.bar = "Hi"
print foo.bar # "Hi"
the code here outputs 20, 20, why the private property can be accessed here:
class myClass {
private $a;
public function __construct() {
$this->a = 10;
}
public function printValue() {
print "The Value is: {$this->a}\n";
}
public function changeValue($val, $obj = null) {
if(is_null($obj)) {
$this->a = $val;
} else {
$obj->a = $val; //why this works?
}
}
public function getValue() {
return $this->a;
}
}
$obj_one = new myClass();
$obj_two = new myClass();
$obj_one->changeValue(20, $obj_two);
$obj_two->changeValue($obj_two->getValue(), $obj_one);
$obj_two->printValue();
$obj_one->printValue();
any ideas?
For the purpose of encapsulation, it's important that the internals of a class be protected from access by other parts of code that must not know about the internals of the class. The class itself presumably knows about its internals and can access private properties of instances of itself just fine.
Class can always access its own properties regardless of whether they belong to the instance itself or to another instance. This works exactly as intended.
This is not an issue. You're not accessing the private property from outside, but inside the class and returning it. This basic OO.
$obj->a = $val; //why this works?
It works because you are passing an object of myClass using $obj_two and inside the class the variable $a can be accessed, which is perfectly fine
The restriction of accessing the private variable is enforced when you try something like:
$obj_two = new myClass();
echo $obj_two->a;
$obj_one->changeValue(20, $obj_two);
$obj_two->changeValue($obj_two->getValue(), $obj_one);
In both lines $obj is not null so the else part is executed. During the first call value of $this->a = 20 and during the second call when you use $obj_two->getValue() it retrieves the value of $obj_two->a which is set when you called the first function.