I have just learned that you can set any property of a class (as long as it's not explicitly declared as a private one). For example:
class Test {}
$test = new Test;
$test->randomthing = 'Hello world';
var_export($test);
Works just fine to set the property randomthing of the $test object.
I am trying to find out what this behaviour is called, and if it's possible to eliminate/disable it (to minimise errors in code), because as it currently stands, i can be typoing my desired properties and not notice in time.
Ideally, i would want to have:
class User {
protected $name;
}
And then i want to be able to set a $user->name = '', but not $user->randomthing = ''. This second call would ideally throw an exception or something.
I have tried to guess this convention by looking up "dynamic properties", but that seems to refer to stuff like the double-dollar syntax to reference a property by a value in a variable.
So, what do you call this behaviour? And where can i read up more about controlling it (or disabling it)?
It doesn't have a special name or anything, it's just assigning a property to an object. This is standard and default PHP - to my knowledge, this isn't something you can disable/turn off.
All we're doing $object->newProp = 'foo' is appending a new key with a value to the object, that key + value pair isn't globally available once it's been added. It's only for that object. It's the same principle for an array:
$array = [1, 2, 3];
$array[] = 4;
We're doing the same with your object:
$object = new Class();
$object->foo = 4;
I would create a "magic" __set() function that doesn't set a parameter, but instead returns an error/warning.
public function __set ( string $name , mixed $value ) {
if( in_array( $name, [ 'various', 'valid', 'properties' ] ) ) {
$this->$name = $value;
} else {
trigger_error( "Setting unknown property ".__CLASS__."->$name is not permitted." );
}
}
https://www.php.net/manual/en/language.oop5.overloading.php#object.set
Related
In perl I'm used to doing
my $foo = new WhatEver( bar => 'baz' );
and now I'm trying to figure out if PHP objects can ever be constructed this way. I only see this:
my $foo = new WhatEver();
$foo->{bar} = 'baz';
is it possible to do it in one step?
You can lay out your constructor as follows:
class MyClass {
public function __construct($obj=null) {
if ($obj && $obj instanceof Traversable || is_array($obj)) {
foreach ($obj as $k => $v) {
if (property_exists($this,$k)) {
$this->{$k} = $v;
}
}
}
}
}
This has a serie of drawbacks:
This is inefficient
The variables you create will not show up on any doc software you use
This is the open door to all forms of slackery
However, it also presents the following benefits:
This can be extended pretty safely
It allows you to lazy-implement variables
It also allows you to set private variables, provided that you know their names. It is pretty good in that respect if not abused.
The parameters passed in the parentheses (which can be omitted, by the way, if there aren't any) go to the constructor method where you can do whatever you please with them. If a class is defined, for example, like this:
class WhatEver
{
public $bar;
public function __construct($bar)
{
$this -> bar = $bar;
}
}
You can then give it whatever values you need.
$foo = new WhatEver('baz');
There are a few ways to accomplish this, but each has its own drawbacks.
If your setters return an instance of the object itself, you can chain your methods.
my $foo = new WhatEver();
$foo->setBar("value")->setBar2("value2");
class WhatEver
{
public $bar;
public $bar2;
public function setBar($bar)
{
$this->bar = $bar;
return $this;
}
public function setBar2($bar2)
{
$this->bar2 = $bar2;
return $this;
}
}
However, this doesn't reduce it to one step, merely condenses every step after instantiation.
See: PHP method chaining?
You could also declare your properties in your constructor, and just pass them to be set at creation.
my $foo = new WhatEver($bar1, $bar2, $bar3);
This however has the drawback of not being overtly extensible. After a handful of parameters, it becomes unmanageable.
A more concise but less efficient way would be to pass one argument that is an associative array, and iterate over it setting each property.
The implicit assumption here is that objects have meaningful, presumably public, properties which it is up to the calling code to provide values for. This is by no means a given - a key aspect of OOP is encapsulation, so that an object's primary access is via its methods.
The "correct" mechanism for initialising an object's state is its constructor, not a series of property assignments. What arguments that constructor takes is up to the class definition.
Now, a constructor might have a long series of named parameters, so that you could write $foo = new WhatEver(1, "hello", false, null) but if you want these to act like options, then it could take a single hash - in PHP terms, an Array - as its argument.
So, to answer the question, yes, if your constructor is of the form function __construct(Array $options) and then iterates over or checks into $options. But it's up to the constructor what to do with those options; for instance passing [ 'use_safe_options' => true ] might trigger a whole set of private variables to be set to documented "safe" values.
As of PHP 5.4 (which introduced [ ... ] as an alternative to array( ... )), it only takes a few more character strokes than the Perl version:
$foo = new WhatEver( ['bar' => 'baz'] );
Assume this class code:
class Foo {
function method() {
echo 'works';
}
}
Is there any way to store a reference to the method method of a Foo instance?
I'm just experimenting and fiddling around, my goal is checking whether PHP allows to call $FooInstance->method() without writing $FooInstance-> every time. I know I could write a function wrapper for this, but I'm more interested in getting a reference to the instance method.
For example, this pseudo-code would theoretically store $foo->method in the $method variable:
$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method
$method();
Apparently, as method is a method and I'm not calling it with () the interpreter thinks I'm looking for a property thus this doesn't work.
I've read through Returning References but the examples only show how to return references to variables, not methods.
Therefore, I've adapted my code to store an anonymous function in a variable and return it:
class Foo {
function &method() {
$fn = function() {
echo 'works';
};
return $fn;
}
}
$foo = new Foo();
$method = &$foo->method();
$method();
This works, but is rather ugly. Also, there's no neat way to call it a single time, as this seems to require storing the returned function in a variable prior to calling it: $foo->method()(); and ($foo->method())(); are syntax errors.
Also, I've tried returning the anonymous function directly without storing it in a variable, but then I get the following notice:
Notice: Only variable references should be returned by reference
Does this mean that returning/storing a reference to a class instance method is impossible/discouraged or am I overlooking something?
Update: I don't mind adding a getter if necessary, the goal is just getting a reference to the method. I've even tried:
class Foo {
var $fn = function() {
echo 'works';
};
function &method() {
return $this->fn;
}
}
But from the unexpected 'function' (T_FUNCTION) error I'd believe that PHP wisely doesn't allow properties to store functions.
I'm starting to believe that my goal isn't easily achievable without the use of ugly hacks as eval().
It is. You have to use an array, with two values: the class instance (or string of the class name if you are calling a static method) and the method name as a string. This is documented on the Callbacks Man page:
A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.
Demo (Codepad):
<?php
class Something {
public function abc() {
echo 'called';
}
}
$some = new Something;
$meth = array($some, 'abc');
$meth(); // 'called'
Note this is also works with the built-ins that require callbacks (Codepad):
class Filter {
public function doFilter($value) {
return $value !== 3;
}
}
$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'
And for static methods -- note the 'Filter' instead of an instance of a class as the first element in the array (Codepad):
class Filter {
public static function doFilter($value) {
return $value !== 3;
}
}
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3
Yes, you can. PHP has a "callable" pseudo-type, which is, in fact, either just a string or an array. Several functions (usort comes to mind) accept a parameter of the "callback" type: in fact, they just want a function name, or an object-method pair.
That's right, strings are callable:
$fn = "strlen";
$fn("string"); // returns 6
As mentioned, it's possible to use an array as a callback, too. In that case, the first element has to be an object, and the second argument must be a method name:
$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()
Previously, you had to use call_user_func to call them, but syntax sugar in recent versions make it possible to perform the call straight on variables.
You can read more on the "callable" documentation page.
No, as far as I know it's not possible to store a reference to a method in PHP. Storing object / class name and a method name in an array works, but it's just an array without any special meaning. You can play with the array as you please, for example:
$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1;
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
this is prone to error as you loose syntax checking due to storing the method name as string.
you can't pass reliably a reference to a private or a protected method this way (unless you call the reference from a context that already has proper access to the method).
Personally I prefer to use lambdas:
$ref = function() use($my_object) { $my_object->x(); }
If you do this from inside $my_object it gets less clunky thanks to access to $this:
$ref = function() { $this->x(); }
this works with protected / private methods
syntax checking works in IDE (less bugs)
unfortunately it's less concise
class Foo
{
public $var ;
function __construct($value)
{
$this->var = $value ;
}
}
$myFoo = new Foo('hello');
echo $myFoo->var . '<br>' ; // output : hello
// Question : how can I prevent another programer from accidentaly doing the following
$myFoo = 4 ;
echo $myFoo ; // output : 4
my question is in the comment // Question :...
I would like my coworkers being able to assign values to $myFoo use only $myFoo->var (or whatever public mutators are available in the class Foo)
thank you
EDIT :
with all respect to the users who claim it is not possible, SPL_Types PECL extention was able to achieve that (to a certain degree) see e.g http://php.net/manual/en/class.splint.php or http://blog.felixdv.com/2008/01/09/spl_types-in-php-and-strong-typing/
You cannot do this in any weakly typed language. If you have functions that take this variable as an argument, you can use type hinting in PHP, but otherwise you cannot prevent people from re-assigning their variables.
This is true to an extent even for strongly typed languages. If a programmer creates two instances of a class, there is no mechanism to prevent them from assigning a different instance to a variable of the same type.
The only way this can happen is if the programmer explicitly uses constants instead of variables (such as using things like final in Java, or val in Scala, etc), but, either way, you have no control over it in any language.
You cannot prevent changing the type within the class, but if you make it protected or private and then add a setVariable() method (where Variable is your variable name) you can control the input. Something like:
class myClass {
protected $integer = 0;
public function setInteger($new_value)
{
if (!is_int($new_value)) {
throw new RuntimeException('Cannot assign non-integer value to myClass::$integer');
}
$this->integer = $new_value;
}
public function getInteger()
{
return $this->integer;
}
}
// Now, outside of the class, the $integer property can only be changed using setInteger()
$class = new myClass;
$class->setInteger('dog'); // Uncaught Runtime exception ...
$class->setInteger(5);
echo $class->getInteger(); // 5
An alternative version of that function would accept string numbers and convert them to integers:
public function setInteger($new_value)
{
if (!is_numeric($new_value)) {
throw new RuntimeException('Cannot assign non-integer value to myClass::$integer');
}
$this->integer = (int) $new_value;
}
$class->setInteger('5'); // 5
I'm trying to dynamically create object properties for JSON representation of an object. The class User will feature some default properties (setted in __construct). I'm using custom object instead of arrays because i prefer object oriented style (and i need also custom setter/getters methods).
However the first try gives me:
Strict standards: Creating default object from empty value.
even if the code actually works (and json_encode shows the right output):
<?php
class User
{
protected $data = array();
public function __set($property, $value)
{
$this->data[$property] = $value;
}
}
$u = new User();
$u->name = "James Smith"; // Works
$u->status->active = false; // Fail
$u->status->modified = time();
var_dump(json_encode($u));
?>
I think it's because the call $u->status->active, when property $u->status does not exist yet. Do you know how to fix this?
OK I sorted that out for you :) It was interesting.
First, you have not initialized the status property. So in theory, this should have been sufficient:
$u->status = new StdClass;
However, it is more complicated than this. Even if you do it, it won't work. That is because you are setting your fields in the data array, but you are never GETTING THEM OUT from there!
So when you access a field ($u->status) you are NOT taking the field you have just set: you are accessing an unset object property. If you try to print $u->name after setting it, you will not get anything, because you have not created a getter function which would read your data array.
You should either create a getter, or delete the setter (it will work anyway, but may not be what you need).
If you comment out the setter, everything works without warnings. See this simplified version:
<?php
error_reporting(E_STRICT);
class User
{
}
$u = new User();
$u->name = "James Smith";
$u->status = new StdClass; // Comment this line and you will get the strict warning
$u->status->active = false;
var_dump($u);
I'm looking for a way to access the default value assignment for a property without instantiating the class.
E.g.
class Foo {
private $bar = 'bar';
}
$reflClass = new ReflectionClass('Foo');
$reflProp = $reflClass->getProperty('bar');
Now what? If I use $reflProp->getValue() (without an object argument) it will fail.
You can use getDefaultProperties():
var_dump($reflClass->getDefaultProperties());