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));
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Reference - What does this symbol mean in PHP?
So I've been reading through the book PHP Solutions, Dynamic Web Design Made Easy by David Powers. I read through the short section on Object Oriented PHP, and I am having a hard time grasping the idea of the -> operator. Can anyone try to give me a solid explanation on the -> operator in OOP PHP?
Example:
$westcost = new DateTimeZone('America/Los_Angeles');
$now->setTimezone($westcoast);
Also,a more general example:
$someObject->propertyName
The -> operator in PHP refers to either a function or a variable inside a class.
<?php
class Example {
public $variableInClass = "stringContent";
public function functionInClass() {
return "functionReturn";
}
}
$example = new Example();
var_dump($example->variableInClass); //stringContent
var_dump($example->functionInClass()); //functionReturn
?>
Do note that if we're talking about static classes (different purpose), you use :: instead:
<?php
class Example {
public static $variableInClass = "stringContent";
public static function functionInClass() {
return "functionReturn";
}
}
var_dump($example::$variableInClass); //stringContent
var_dump($example::functionInClass()); //functionReturn
?>
$someObject->propertyName can be read as:
return value stored in propertyName from object $someObject
$someObject->methodName() can be read as:
execute methodName from object $someObject
Classes and objects 101:
A class is defined as such:
class MyClass {
public $value1;
public function getValue() {
return $this->value;
}
}
We now defined a class with a single property, and a single function. To use these, we need to create an 'instance' of this object:
$myObject = new MyClass();
To use the property or function, we use the -> operator:
echo $myObject->value1;
echo $myObject->getValue();
Put a little bit more abstractly.. the function getValue is defined in this object. By using the -> operator on an instance of our class, what PHP does is effectively just call the function, just like any other function.. but before it gets called $this is assigned to the current object.
Hope this helps, if not.. I would simply recommend reading about OOP basics.
Is it possible to instantiate a class from a string, without declaring another variable before ?
It's usually done writing
$className = 'myClass'
$instance = new $className();
but it could be handy to have it shorter like for example
$instance = new ${'className'}();
The purpose is to have objects created (under condition) inside a loop without use of extra vars...
Edit : $className is dynamic, it is hard coded above to explain the situation
See factory pattern.
class Foo {
static function factory($class, array $args = null) {
return new $class($args);
}
}
// class factoring; returns a new instance of requested class ($className)
Foo::factory($className);
I added optional arguments array if you want to set some class properties.
// pass some values for class constructor
Foo::factory($className, array('arg1' => 1, 'arg2' => 2, 'args3' => 3));
Furthermore, you can build "fluid" interfaces so you can "chain" methods when you use that pattern:
Foo::factory($className)->method1()->method2(array('param' => 'value'))->etc();
where method1(), method2() must return $this (the object itself) to chain multiple method calls in one line.
You could make a factory function (or class/method) that takes a class name as a parameter, and then call it with the result of your dynamic PHP code that generates the string. You might consider it a bit cleaner but it's not going to save you any memory or speed.
class foo { }
function factory($class) { return new $class(); }
foreach (...) {
$instance = factory(<some code that returns the string 'foo'>);
}
It's one extra variable, does it really make much of a difference? The answer is that unless you use eval (which comes with security issues) it isn't possible to do it any shorter than your first example.
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
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.
I'm looking for a functionality, in the example below called "theFunctionILookFor", that will make the code work.
$myClassName = "someName";
$parentOrInterfaceName = "someParent";
if (theFunctionILookFor($myClassName)) {
echo "is parent";
}
Edit: I try to avoid instantiating the class just to perform this check. I would like to be able to pass 2 string parameters to the checking function
Looks like this is my answer: is_subclass_of
It can accept both parameters as strings
http://php.net/manual/en/function.is-subclass-of.php
Try is_subclass_of(). It can accept both parameters as strings. http://php.net/manual/en/function.is-subclass-of.php
Using the Reflection API, you can construct a ReflectionClass object using the name of the class as well as an object.
You might use this?
http://www.php.net/manual/en/function.get-parent-class.php
From the page:
get_parent_class
(PHP 4, PHP 5, PHP 7)
get_parent_class — Retrieves the parent class
name for object or class
Description
string get_parent_class ([ mixed $object ] )
Retrieves the parent
class name for object or class.
This is an old thread, but still for future reference, you can use the functions is_a and is_subclass_of to accomplish this in a more efficient way.
No need to instantiate anything, you can just pass your string like:
if (is_subclass_of($myObject, 'SomeClass')) // is_a(...)
echo "yes, \$myObject is a subclass of SomeClass\n";
else
echo "no, \$myObject is not a subclass of SomeClass\n";
PHP Docs:
http://www.php.net/manual/en/function.is-a.php
http://www.php.net/manual/en/function.is-subclass-of.php
I think that instanceof is a lot faster and more clear to read.
instanceof does not work on strings, because it can be used as a "identifier" for a class:
$temp = 'tester';
$object = new sub();
var_dump($object instanceof $temp); //prints "true" on php 5.3.8
class tester{}
class sub extends \tester{
}
Also, it works on interfaces - you just need a variable to contain the absolute class name (including namespace, if you want to be 100% sure).
Maybe it's old question, but problem still is actual, here, below, the best solution I've found
class ProductAttributeCaster
{
public function cast(mixed $value): mixed
{
if ($value instanceof ($this->castTo())) {
// do stuff
}
return $value;
}
function castTo(): string
{
return ProductAttribute::class;
}
}