I'm wondering if there's a short notation in PHP for getting an object field when creating an object.
For example, in Java, I don't have to put a newly created object in a variable in order to get one of it's fields.
Example:
public class NewClass {
public int testNum = 5;
}
now, to get the testNum field in a newly created object all I have to do is:
int num = (new NewClass()).testNum;
While a similar case in PHP would force me to do this:
$obj = new NewClass();
$num = $obj->testNum;
Is there a way in PHP to do it in one statement?
Note: I cannot edit the classes.
Maybe you are looking for either static properties, or constants
public class NewClass {
const NUM = 5;
public static $num = 5;
}
$num = NewClass::NUM;
$num = NewClass::$num;
If you are really need object members, then no, PHP currently doesn't support this, but its scheduled for the next 5.4 release.
You can create a wrapper create function in your class that calls the constructor, then you can simply:
$num = NewClass::Create()->testNum;
You can do it only with functions/methods calls.
new is not a function, but a language construct.
Related
What I would like to do after creating an instance of a class is to be able to call the name of that instance as a function. For example, consider the following class Foo:
$bar = new Foo(5); // generates 5 random ints between 0-100
bar(3); // get the third int in the object bar
Is this even possible in PHP or would it involve messing with the parser? Thanks in advance!
What this question is really about is creating a PHP functor and here's an example I lifted from here:
<?php
class SquareCallback
{
public function __invoke($value)
{
return $value * $value;
}
}
$squareObject = new SquareCallback;
var_dump($squareObject(3));
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();
}
I have the following class with several properties and a method in PHP (This is simplified code).
class Member{
public $Name;
public $Family;
public function Fetch_Name(){
for($i=0;$i<10;$i++){
$this[$i]->$Name = I find the name using RegExp and return the value to be stored here;
$this[$i]->Family = I find the family using RegExp and return the value to be stored here;
}
}//function
}//class
In the function Fetch_Name(), I want to find all the names and families that is in a text file using RegExp and store them as properties of object in the form of an array. But I don't know how should I define an array of the Member. Is it logical or I should define StdClass or 2-dimension array instead of class?
I found slightly similar discussion here, but a 2 dimensional array is used instead of storing data in the object using class properties.
I think my problem is in defining the following lines of code.
$Member = new Member();
$Member->Fetch_name();
The member that I have defined is not an array. If I do define it array, still it does not work. I did this
$Member[]= new Member();
But it gives error
Fatal error: Call to a member function Fetch_name() on a non-object in
if I give $Member[0]= new Member() then I don't know how to make $Member1 or Member[2] or so forth in the Fetch_Name function. I hope my question is not complex and illogical.
Many thanks in advance
A Member object represents one member. You're trying to overload it to represent or handle many members, which doesn't really make sense. In the end you'll want to end up with an array that holds many Member instances, not the other way around:
$members = array();
for (...) {
$members[] = new Member($name, $family);
}
Most likely you don't really need your Member class to do anything really; the extraction logic should reside outside of the Member class, perhaps in an Extractor class or something similar. From the outside, your code should likely look like this:
$parser = new TextFileParser('my_file.txt');
$members = $parser->extractMembers();
I think you should have two classes :
The first one, Fetcher (or call it as you like), with your function.
The second one, Member, with the properties Name and Family.
It is not the job of a Member to fetch in your text, that's why I would make another class.
In your function, do your job, and in the loop, do this :
for($i = 0; $i < 10; ++$i){
$member = new Member();
$member->setName($name);
$member->setFamily($family);
// The following is an example, do what you want with the generated Member
$this->members[$i] = $member;
}
The problem here is that you are not using the object of type Member as array correctly. The correct format of your code would be:
class Member{
public $Name;
public $Family;
public function Fetch_Name(){
for($i=0;$i<10;$i++){
$this->Name[$i] = 'I find the name using RegExp and return the value to be stored here';
$this->Family[$i] = 'I find the family using RegExp and return the value to be stored here';
}
}
}
First, $this->Name not $this->$Name because Name is already declared as a member variable and $this->Name[$i] is the correct syntax because $this reference to the current object, it cannot be converted to array, as itself. The array must be contained in the member variable.
L.E: I might add that You are not writing your code according to PHP naming standards. This does not affect your functionality, but it is good practice to write your code in the standard way. After all, there is a purpose of having a standard.
Here you have a guide on how to do that.
And I would write your code like this:
class Member{
public $name;
public $family;
public function fetchName(){
for($i=0;$i<10;$i++){
$this->name[$i] = 'I find the name using RegExp and return the value to be stored here';
$this->family[$i] = 'I find the family using RegExp and return the value to be stored here';
}
}
}
L.E2: Seeing what you comented above, I will modify my answer like this:
So you are saying that you have an object of which values must be stored into an array, after the call. Well, after is the key word here:
Initialize your object var:
$member = new Memeber();
$memebr->fechNames();
Initialize and array in foreach
$Member = new Member();
foreach ($Member->Name as $member_name){
$array['names'][] = $member_name;
}
foreach ($Member->Family as $member_family) {
$array['family'][] = $member_family;
}
var_dump($array);
Is this more of what you wanted?
Hope it helps!
Keep on coding!
Ares.
Codeigniter can return a database query as generic "Object" like:
$q = $this->db->get("some_table");
$obj = $this->q->row();
$var = $obj->some_property
In my case I want to make a PHP class who's public variables are 1 for 1 with the database columns, along with some public methods. Is there a quick one-shot way to cast or convert the generic "Row" object into my custom class object? I've read posts that hint that it is certainly possible, but most involve a really hacky serialize/deserialize solution. In the past I have just done:
public function __construct($row) {
$this->prop = $row->prop;
$this->id = $row->id;
$this->value = $row->value;
}
And I find this is very tedious and makes ugly code.
See the third section under result():
CodeIgniter User Guide: Generating Query Results
You can also pass a string to result() which represents a class to instantiate for each result object (note: this class must be loaded)
$query = $this->db->query("SELECT * FROM users;");
foreach ($query->result('User') as $row)
{
echo $row->name; // call attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
}
This question already has an answer here:
How to call the constructor with call_user_func_array in PHP
(1 answer)
Closed 7 years ago.
I have searched many a page of Google results as well as here on stackoverflow but cannot find a solution that seems to fit my situation. I appear to have but one last snag in the function I am trying to build, which uses call_user_func_array to dynamically create objects.
The catchable fatal error I am getting is Object of class Product could not be converted to string. When the error occurs, in the log I get five of these (one for each argument): PHP Warning: Missing argument 1 for Product::__construct(), before the catchable fatal error.
This is the code of the function:
public static function SelectAll($class, $table, $sort_field, $sort_order = "ASC")
{
/* First, the function performs a MySQL query using the provided arguments. */
$query = "SELECT * FROM " .$table. " ORDER BY " .$sort_field. " " .$sort_order;
$result = mysql_query($query);
/* Next, the function dynamically gathers the appropriate number and names of properties. */
$num_fields = mysql_num_fields($result);
for($i=0; $i < ($num_fields); $i++)
{
$fetch = mysql_fetch_field($result, $i);
$properties[$i] = $fetch->name;
}
/* Finally, the function produces and returns an array of constructed objects.*/
while($row = mysql_fetch_assoc($result))
{
for($i=0; $i < ($num_fields); $i++)
{
$args[$i] = $row[$properties[$i]];
}
$array[] = call_user_func_array (new $class, $args);
}
return $array;
}
Now, if I comment out the call_user_func_array line and replace it with this:
$array[] = new $class($args[0],$args[1],$args[2],$args[3],$args[4]);
The page loads as it should, and populates the table I am building. So everything is absolutely functional until I try to actually use my $args array within call_user_func_array.
Is there some subtle detail about calling that array that I am missing? I read the PHP manual for call_user_func_array once, and then some, and examples on that page seemed to show people just building an array and calling it for the second argument. What could I be doing wrong?
You can't call the constructor of $class like this:
call_user_func_array (new $class, $args);
That's no valid callback as first parameter. Let's pick this apart:
call_user_func_array (new $class, $args);
Is the same as
$obj = new $class;
call_user_func_array ($obj, $args);
As you can see, the constructor of $class has been already called before call_user_func_array comes into action. As it has no parameters, you see this error message:
Missing argument 1 for Product::__construct()
Next to that, $obj is of type object. A valid callback must be either a string or an array (or exceptionally a very special object: Closure, but that's out of discussion here, I only name it for completeness).
As $obj is an object and not a valid callback, so you see the PHP error message:
Object of class Product could not be converted to string.
PHP tries to convert the object to string, which it does not allow.
So as you can see, you can't easily create a callback for a constructor, as the object yet not exists. Perhaps that's why you were not able to look it up in the manual easily.
Constructors need some special dealing here: If you need to pass variable arguments to a class constructor of a not-yet initialize object, you can use the ReflectionClass to do this:
$ref = new ReflectionClass($class);
$new = $ref->newInstanceArgs($args);
See ReflectionClass::newInstanceArgs
Not possible using call_user_func_array(), because (as the name suggest) it calls functions/methods, but is not intended to create objects, Use ReflectionClass
$refClass = new ReflectionClass($class);
$object = $refClass->newInstanceArgs($args);
Another (more design-based) solution is a static factory method
class MyClass () {
public static function create ($args) {
return new self($args[0],$args[1],$args[2],$args[3],$args[4]);
}
}
and then just
$object = $class::create($args);
In my eyes it's cleaner, because less magic and more control
I use this for singleton factory pattern, becouse the ReflectionClass brokes the dependence tree, I hate the use of eval but its the only way to i find to simplificate the use of singleton pattern to inject mockObjects whith PHPUnit whitout open the class methods to that injection, BE CAREFULL WHITH THE DATA WHAT YOU PASS TO eval FUNCTION!!!!!!!! YOU MUST BE SURE THAT IS CLEANED AND FILTERED!!!
abstract class Singleton{
private static $instance=array();//collection of singleton objects instances
protected function __construct(){}//to allow call to extended constructor only from dependence tree
private function __clone(){}//to disallow duplicate
private function __wakeup(){}//comment this if you want to mock the object whith php unit jejeje
//AND HERE WE GO!!!
public static function getInstance(){
$a=get_called_class();
if(!array_key_exists($a, self::$instance)){
if(func_num_args()){
/**HERE IS THE CODE **//
$args=func_get_args();
$str='self::$instance[$a]=new $a(';
for($i=0;$i<count($args);$i++){
$str.=(($i)?",":"").'$args['.$i.']';
}
eval($str.");");//DANGER, BE CAREFULLY...we only use this code to inject MockObjects in testing...to another use you will use a normal method to configure the SingletonObject
/*--------------------------*/
}else{
self::$instance[$a]=new $a();
}
}
return self::$instance[$a];
}
}
And to use that:
class MyClass extends Singleton{
protected function __construct(MyDependInjection $injection){
//here i use the args like a normal class but the method IS PROTECTED!!!
}
}
to instanciate the object:
$myVar= MyClass::getInstance($objetFromClassMyDependInjection);
it calls the constructor whith the args I pased. i know that i can get the same result extending the static method getInstance but to teamworking its more easy to use this way