I have the following issue:
The current code of an application I'm working on contains a very large number of definitions like this:
$obj = new stdClass();
$obj->a->b = "something";
This results in: PHP Strict Standards: Creating default object from empty value in [somewhere].
The correct form would be:
$obj = new stdClass();
$obj->a = new stdClass();
$obj->a->b = "something";
Now the problem: Replacing this throughout the code would take ages (consider thousands of cases, with conditions, etc.).
So I was thinking of replacing stdClass with a custom object (this would be a simple replace in code), creating a setter for it that verifies if the variable property is an object, and defines it as object if it is before setting the second property.
So we get:
class MockObject() {
public function __set($property, $value) {
// Do stuff here
}
}
$obj = new MockObject();
$object->a->b = "something";
The problem is that when executing $object->a->b = "something"; setter is not called (because you don't actually set the a property, but the b property).
Is there any way around this? Or is other solution possible?
Note: Explicitly calling the __set() method is not a solution since it would be the same as defining properties as stdClass().
You know about the magic setter.
Use a magic getter also.
If it wants to get a var that does not exists: create one (in an array or something like that) that is an instance of that class.
Why don't you initialize your b variable in the constructor of the A class ?
public function __construct()
{
$this->b = new B();
}
Related
I've seen two like these:
$this->propertyName;
$this->methodName();
And I know the code should look like this:
class myClass{
public $propertyName;
public function methodName(){
// statements
}
}
Recently I saw a code like this:
$this->CI->form_validation->set_message('investigate', '{field} ' . lang('wrong'));
What is CI (a method?)? What's form_validation? What's set_message? Are these nested methods? How does its code look like?
A method can return an object. You can assign this object to a variable and then call methods on it:
$objB = $objA->getObjectB();
$objB->someMethod();
Or you can skip the assignment and just inline it;
$objA->getObjectB()->someMethod();
As long as your methods return objects, you can just keep stacking them up:
$objA->getObjectB()->getObjectC()->getObjectD()->getObjectE();
The same holds true for object properties. A public property can hold another object:
$objA->objB = new objectB();
$objA->objB->someMethodOnObjectB();
So methods that return an object and public properties that contain an object can be stacked like this. You can mix them too:
$objA->getObjectB()->somePropertyOnObjectB->someMethod();
Also see Fluent Interface. It's common for objects to return instances of themselves for mutator methods. This allows you to turn this:
$obj = new Object();
$obj->setFoo('foo');
$obj->setBar('bar');
$obj->setBaz('baz');
$obj->doSomething();
Into this:
$obj = new Object();
$obj->setFoo('foo')->setBar('bar')->setBaz('baz')->doSomething();
And in PHP 5.6+, you don't even have to assign the object to a variable, you can dereference the new call directly:
(new Object())->setFoo('foo')->setBar('bar')->setBaz('baz')->doSomething();
CI is a object. In this exact context it is a class property, object type. Theoretically you may have also a method/function that return an object. Something like:
$ClassInstance->MethodThatReturnAnObject()->ObjectProperty = 'bla';
or even
$ClassInstance->MethodThatReturnAnObject()->ObjectMethod();
And the chain may continue with more levels.
How to combine two variables to obtain / create new variable?
public $show_diary = 'my';
private my_diary(){
return 1;
}
public view_diary(){
return ${"this->"}.$this->show_diary.{"_diary()"}; // 1
return $this->.{"$this->show_diary"}._diary() // 2
}
both return nothing.
Your class should be like following:
class Test
{
public $show_diary;
function __construct()
{
$this->show_diary = "my";
}
private function my_diary(){
return 707;
}
public function view_diary(){
echo $this->{$this->show_diary."_diary"}(); // 707
}
}
It almost looks from your question like you are asking about how to turn simple variables into objects and then how to have one object contain another one. I could be way off, but I hope not:
So, first off, what is the differnce between an object and a simple variable? An object is really a collection of (generally) at least one property, which is sort of like a variable within it, and very often functions which do things to the properties of the object. Basically an object is like a complex variable.
In PHP, we need to first declare the strucutre of the object, this is done via a class statement, where we basicaly put the skeleton of what the object will be into place. This is done by the class statement. However, at this point, it hasn't actually been created, it is just like a plan for it when it is created later.
The creation is done via a command like:
$someVariable= new diary();
This executes so create a new variable, and lays it out with the structure, properties and functions defined in the class statement.
From then on, you can access various properties or call functions within it.
class show_diary
{
public $owner;
public function __construct()
{
$this->owner='My';
}
}
class view_diary
{
public $owner;
public $foo;
public function __construct()
{
$this->foo='bar';
$this->owner=new show_diary();
}
}
$diary= new view_diary();
print_r($diary);
The code gives us two classes. One of the classes has an instance of the other class within it.
I have used constructors, which are a special type of function that is executed each time we create a new instance of a class - basically each time we declare a variable of that type, the __construct function is called.
When the $diary= new view_diary(); code is called, it creates an instance of the view_diary class, and in doing so, the first thing it does is assigns it's own foo property to have the value 'bar' in it. Then, it sets it's owner property to be an instance of show_diary which in turn then kicks off the __construct function within the new instance. That in turn assigns the owner property of the child item to have the value 'My'.
If you want to access single properties of the object, you can do so by the following syntax:
echo $diary->foo;
To access a property of an object inside the object, you simply add more arrows:
echo $diary->owner->owner;
Like this?
$diary = $this->show_diary . '_diary';
return $this->$diary();
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
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());