How do I get a property in a PHP based on a string? I'll call it magic. So what is magic?
$obj->Name = 'something';
$get = $obj->Name;
would be like...
magic($obj, 'Name', 'something');
$get = magic($obj, 'Name');
Like this
<?php
$prop = 'Name';
echo $obj->$prop;
Or, if you have control over the class, implement the ArrayAccess interface and just do this
echo $obj['Name'];
If you want to access the property without creating an intermediate variable, use the {} notation:
$something = $object->{'something'};
That also allows you to build the property name in a loop for example:
for ($i = 0; $i < 5; $i++) {
$something = $object->{'something' . $i};
// ...
}
What you're asking about is called Variable Variables. All you need to do is store your string in a variable and access it like so:
$Class = 'MyCustomClass';
$Property = 'Name';
$List = array('Name');
$Object = new $Class();
// All of these will echo the same property
echo $Object->$Property; // Evaluates to $Object->Name
echo $Object->{$List[0]}; // Use if your variable is in an array
Something like this? Haven't tested it but should work fine.
function magic($obj, $var, $value = NULL)
{
if($value == NULL)
{
return $obj->$var;
}
else
{
$obj->$var = $value;
}
}
Just store the property name in a variable, and use the variable to access the property. Like this:
$name = 'Name';
$obj->$name = 'something';
$get = $obj->$name;
There might be answers to this question, but you may want to see these migrations to PHP 7
source: php.net
It is simple, $obj->{$obj->Name} the curly brackets will wrap the property much like a variable variable.
This was a top search. But did not resolve my question, which was using $this. In the case of my circumstance using the curly bracket also helped...
example with Code Igniter get instance
in an sourced library class called something with a parent class instance
$this->someClass='something';
$this->someID=34;
the library class needing to source from another class also with the parents instance
echo $this->CI->{$this->someClass}->{$this->someID};
Just as an addition:
This way you can access properties with names that would be otherwise unusable$x = new StdClass;
$prop = 'a b';
$x->$prop = 1;
$x->{'x y'} = 2;
var_dump($x);object(stdClass)#1 (2) {
["a b"]=>
int(1)
["x y"]=>
int(2)
}(not that you should, but in case you have to).
If you want to do even fancier stuff you should look into reflection
In case anyone else wants to find a deep property of unknown depth, I came up with the below without needing to loop through all known properties of all children.
For example, to find $foo->Bar->baz->bam, given an object ($foo) and a string like "Bar->baz->bam".
trait PropertyGetter {
public function getProperty($pathString, $delimiter = '->') {
//split the string into an array
$pathArray = explode($delimiter, $pathString);
//get the first and last of the array
$first = array_shift($pathArray);
$last = array_pop($pathArray);
//if the array is now empty, we can access simply without a loop
if(count($pathArray) == 0){
return $this->{$first}->{$last};
}
//we need to go deeper
//$tmp = $this->Foo
$tmp = $this->{$first};
foreach($pathArray as $deeper) {
//re-assign $tmp to be the next level of the object
// $tmp = $Foo->Bar --- then $tmp = $tmp->baz
$tmp = $tmp->{$deeper};
}
//now we are at the level we need to be and can access the property
return $tmp->{$last};
}
}
And then call with something like:
$foo = new SomeClass(); // this class imports PropertyGetter trait
echo $foo->getProperty("bar->baz->bam");
Here is my attempt. It has some common 'stupidity' checks built in, making sure you don't try to set or get a member which isn't available.
You could move those 'property_exists' checks to __set and __get respectively and call them directly within magic().
<?php
class Foo {
public $Name;
public function magic($member, $value = NULL) {
if ($value != NULL) {
if (!property_exists($this, $member)) {
trigger_error('Undefined property via magic(): ' .
$member, E_USER_ERROR);
return NULL;
}
$this->$member = $value;
} else {
if (!property_exists($this, $member)) {
trigger_error('Undefined property via magic(): ' .
$member, E_USER_ERROR);
return NULL;
}
return $this->$member;
}
}
};
$f = new Foo();
$f->magic("Name", "Something");
echo $f->magic("Name") , "\n";
// error
$f->magic("Fame", "Something");
echo $f->magic("Fame") , "\n";
?>
What this function does is it checks if the property exist on this class of any of his child's, and if so it gets the value otherwise it returns null.
So now the properties are optional and dynamic.
/**
* check if property is defined on this class or any of it's childes and return it
*
* #param $property
*
* #return bool
*/
private function getIfExist($property)
{
$value = null;
$propertiesArray = get_object_vars($this);
if(array_has($propertiesArray, $property)){
$value = $propertiesArray[$property];
}
return $value;
}
Usage:
const CONFIG_FILE_PATH_PROPERTY = 'configFilePath';
$configFilePath = $this->getIfExist(self::CONFIG_FILE_PATH_PROPERTY);
$classname = "myclass";
$obj = new $classname($params);
$variable_name = "my_member_variable";
$val = $obj->$variable_name; //do care about the level(private,public,protected)
$func_name = "myFunction";
$val = $obj->$func_name($parameters);
why edit:
before : using eval (evil)
after : no eval at all. being old in this language.
Related
i want to turn a simple string like that "response->dict->words" into a variable name that i can actually work with. I will give an example now. Lets assume the value of $response->dict->words is 67.
Example:
$var = "response->dict->words"
echo $$var; /* PRINT THE VALUE 67 FROM $response->dict->words*/
As you may notice i put an extra dollar sign before the $var because this should work, but it doesn't.
Can anyone help me with this?
class ClassOne {
public function test() {
return 'test';
}
}
class ClassTwo {
public function test2() {
return 'test2';
}
}
$one = new ClassOne();
$two = new ClassTwo();
$objects = array('one', 'two');
$methods = array('test', 'test2');
for ($i = 0; $i < count($objects); $i++) {
echo ${$objects[$i]}->$methods[$i]();
}
You can store classnames or method names as strings and later use them, or even store variable names, like here ${$objects} (variable variables), but you cannot store whole logic.
To evaluate whole logic, you have to use eval(), which is most probably bad idea
$var = "response->dict->words"
eval("?> <?php echo $".$var.";");
You can split your string and make the call as below:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = 'words test';
}
}
$response = new Response();
$var = 'response->dict->words';
$elements = explode('->', $var);
echo ${$elements[0]}->$elements[1]->$elements[2];
Results into words test
Or, if you don't know the level of nesting the object call, you can perform the call in a foreach loop. When the loop exits, the last call will be available after it:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = new stdClass();
$this->dict->words->final = 'test chained string';
}
}
$response = new Response();
$var = 'response->dict->words->final';
$elements = explode('->', $var);
foreach ($elements as $key => $element) {
if ($key == 0) {
$call = ${$element};
continue;
}
$call = $call->$element;
}
echo $call;
Results into: test chained string
There is a better way, why don't you cache the variable like
$var = $response->dict->words;
I am aware that a class cannot be defined within a class in php, however I'm curious if there's another way to achieve the desired effect.
I currently have a set of 3 objects used to conduct a search. The first is called $search_request. It contains properties like $keywords (string), $search_results_per_page (int), $page_requested (int), $owner_id (int)
I also have an object called $search_result, it contains properties like $total_matches (int), $result_set (array of objects)
Finally I have the $search_handler object which contains the $search_request and $search_result, along with functions that build the $search_result based on the $search_request.
Usage goes like so:
$search_handler = new search_handler();
$search_handler->search_request->keywords = "cats, dogs";
$search_handler->search_request->search_results_per_page = 10;
$search_handler->search_request->page_search_requested = 1;
$search_handler->get_search_result();
echo $search_handler->search_result->total_matches;
foreach($search_handler->search_result->result_set)
{
//do something
}
All of this works fine. The problem is I want to repeat this model for different objects, so currently I'm forced to use the hackey solution of the "search_" prefix on each class.
I'd like to have something like:
class search
{
public class request
{
$keywords = "";
$search_results_per_page = 5;
$page_requested = 1;
}
public class result
{
$total_matches = null;
$result_set = array();
}
public get_results()
{
//check cache first
$cached = look_in_cache(md5(serialize($this->request)));
if($cached)
{
$this->result->result_set = $cached;
$count = count($cached);
$this->result->total_matches = $count;
}
else
{
//look in db
$results = get_results_from_database($this->request->keywords); //db call goes here
$this->result->result_set = $results;
$count = count($results);
$this->result->total_matches = $count;
}
}
}
//usage
$search = new search();
$search->request->keywords = "cats, dogs";
$search->request->search_results_per_page = 10;
$search->request->page_search_requested = 1;
$search->get_results();
echo $search->results->total_matches;
foreach($search->results->result_set as $result)
{
//do something
}
If for whatever reason you don't want to have those classes you need in different files you can do the following
<?php
class search
{
var $request;
var $result;
public function __construct()
{
$this->request = new StdClass();
$this->request->keywords = "";
$this->request->search_results_per_page = 5;
$this->request->page_requested = 1;
$this->result = new StdClass();
$this->result->total_matches = null;
$this->result->result_set = array();
}
// ...
}
$search = new search();
var_dump($search->request, $search->result);
?>
There's several things to suggest here
First, if you're using PHP 5.3 or later, consider using namespaces and autoloading. That would allow you to make classes like \Cat\Search and \Dog\Search.
Second, I would avoid setting object properties directly. I would highly suggest you make your variables protected and use getters and setters instead. Limit your object interaction to methods and you can control the process much more easily.
$search_handler = new search_handler();
$search_handler->setKeywords(array("cats", "dogs"));
You could define your request and result classes as separately, and then have variables in your main class be assigned to instances of those.
Something like:
class result{
...
}
class request{
...
}
class search{
var results;
var request;
function __construct(){
$results = new ArrayObject();
$request = new stdClass();
}
...
}
Then you can assign/append to the list of results in your search function
and you can just assign a request object to your 'request' variable
$this->results[] = $newresult;
and loop through them when displaying
To avoid getting the error message as in this previous question, I decided to change the class with __get() like this below,
class property
{
public function __get($name)
{
return isset($this->$name) ? $this->$name : new property;
}
}
class objectify
{
public function array_to_object($array = array(), $property_overloading = false)
{
# if $array is not an array, let's make it array with one value of former $array.
if (!is_array($array)) $array = array($array);
# Use property overloading to handle inaccessible properties, if overloading is set to be true.
# Else use std object.
if($property_overloading === true) $object = new property();
else $object = new stdClass();
foreach($array as $key => $value)
{
$key = (string) $key ;
$object->$key = is_array($value) ? self::array_to_object($value, $property_overloading) : $value;
}
return $object;
}
}
$object = new objectify();
$type = null;
$type = $object->array_to_object($type,true);
var_dump($type->a->b->c);
so I get this result in the end,
object(property)#3 (0) { }
but it is still not perfect. as my understanding, the above solution processes the object in a chain like this,
$type = object{}->object{}->object{}
so I wonder if I can find whether it is the last chain and it is empty then just output a null?
$type = object{}->object{}->NULL
is it possible with PHP?
EDIT:
I have thought of an idea which is to count how many times the property class has been instantiated,
class property
{
public static $counter = 0;
function __construct() {
self::$counter++;
}
public function __get($name)
{
if(isset($this->$name))
{
return $this->$name;
}
elseif(property::$counter < 3)
{
return new property;
}
else
{
return null;
}
}
}
but my only problem is how to make the number 3 dynamic. Any ideas?
Sounds like you're looking for a PHP version of Groovy's ?. operator: http://groovy.codehaus.org/Null+Object+Pattern
Afaik, you can't overload or create a new operator in PHP. You could perhaps simulate it by passing all of your nested calls to a function, and the function knows when to return null.
Edit: other options posted here - http://justafewlines.com/2009/10/groovys-operator-in-php-sort-of/
I have a array like this in a function:
$value = array("name"=>"test", "age"=>"00");
I made this $value as public inside the class abc.
Now in my other file, I want to access the values from this array, so I create an instance by:
$getValue = new <classname>;
$getValue->value..
I'm not sure how to proceed so that then I can access each element from that array.
You mentioned that $value is in a function, but is public. Can you post the function, or clarify whether you meant declaring or instantiating within a function?
If you're instantiating it that's perfectly fine, and you can use the array keys to index $value just like any other array:
$object = new classname;
$name = $object->value["name"];
$age = $object->value["age"];
// Or you can use foreach, getting both key and value
foreach ($object->value as $key => $value) {
echo $key . ": " . $value;
}
However, if you're talking about declaring public $value in a function then that's a syntax error.
Furthermore if you declare $value (within a function) without the public modifier then its scope is limited to that function and it cannot be public. The array will go out of scope at the end of the function and for all intents and purposes cease to exist.
If this part seems confusing I recommend reading up on visibility in PHP.
The same as you would normally use an array.
$getValue = new yourClass();
$getValue->value['name'];
Use code
foreach($getValue->value as $key=>$value)
<?php
interface Nameable {
public function getName($i);
public function setName($a,$name);
}
class Book implements Nameable {
private $name=array();
public function getName($i) {
return $this->name[$i];
}
public function setName($i, $name) {
return $this->name[$i] = $name;
}
}
$interfaces = class_implements('Book');
if (isset($interfaces['Nameable'])) {
$bk1 = new Book;
$books = array('bk1', 'bk2', 'bk3', 'bk4', 'bk5');
for ($i = 0; $i < count($books); $i++)
$bk1->setName($i, $books[$i]);
for ($i = 0; $i < count($books); $i++)
echo '// Book implements Nameable: ' . $bk1->getName($i) . nl();
}
?>
I don't want to give an initial value. I want to set these later using the set method
class Duck {
var int id;
var set = Array();
}
Any idea how to declare without getting error?
Your question is marked PHP but it doesn't look like any PHP I know. This is correct PHP:
class Duck {
private $id;
private $set = array();
}
You don't need to specify an initial value for $set as in this example but that just means it gets the standard default value of 0, false, array(), etc depending on how it's used so I'm not sure what you're trying to achieve.
You're typically better off being explicit.
class Duck { var $int, $set = array(); }
Much more PHP is:
class Duck {
public $id;
}
$d = new Duck;
$d->id = 5;
A method could be Typ checking;
private $props = array();
static $types = array ("id" => "is_integer","name" => "is_string");
public function __set($name,$value)
{
if(array_key_exists($name, self::$types))
{
if(call_user_func(self::$types[$name],$value))
{
$this->props[$name] = $value;
}
else
{
print "Type assignment error\n";
debug_print_backtrace();
}
}
}