How to access a two-level objected using variables? - php

Example objects:
$this->obj1->lvl1
$this->obj1->lvl1->lvl2
I know I can access objects like this:
$var = 'obj1';
$this->{$var}
But I want to go further. The problem is that it needs to be dynamic so the name needs to come from a string. I'm using this for mapping. So a user can use dot notations to access anything in the object. So if the user uses this notation:
'obj1.lvl1'
'obj.lvl1.lvl2'
So all I have to do is:
$this->obj1->{$mapped_string}
So $mapped_string can go either one level or two or more levels deep.
It will map directly to the object. Anyone know how I can accomplish this?

Split the string into accessors, and then drill down in a loop. This works for any length of accessors:
$obj = $this;
$accessors = explode('.', $mapped_string);
foreach ($accessors as $acc) {
$obj = $obj->{$acc};
}
var_dump($obj);

Related

Merge two PHP tags

I want to convert two different PHP tags into a single PHP tag.This may sound a little weird but recently I found a question like this and answer with correct marked.
I can't find I'm currently lost address.
My question:
for example;
$gates = array('t1','t2','t3',t4','t5');
$openGates-> and $gates merge.
Result:
$openGates->t1; or t2,t3.
If I remember correctly, the answer to the problem I found before was;
$openGates->{$gates}; like this. I not sure
How can I do that?
You seem to be confusing objects with arrays. Arrays simply contain data, unless you create them with keys also. Such as:
$gates = array('t1'=>true,'t2'=>false,'t3'=>"maybe",'t4'=>0,'t5'=>50);
Matthew Page is correct in that you should look up PHP OOP for a solution.
That being said, you can cast your array to an object providing it has both keys and values:
$gates = (object) array('t1'=>true,'t2'=>false,'t3'=>"maybe",'t4'=>0,'t5'=>50);
or
$openGates = (object) $gates;
This will allow you to access 'properties' of the object in the way you have demonstrated:
echo $openGates->t1;, for example. The -> operator only applies to objects, which are instances of classes, not arrays.
If you do cast your array to the type of object, be sure that you have both keys and values.
It's not simple for newbie programmer...
At first:
$gates = array('t1','t2','t3','t4','t5');
It's Array
$openGates->
This is class instance. Btw. You can retrieve class instance var like $className->varName
You can't simple merge array and class instance. But you can create new class instance variables by loop.
foreach($gates as $gateKey=>$gateVal) {
$openGates->$gatesVal = NULL;
}
But I think it's, in result should be like that:
$gates = array('t1'=>'opened','t2'=>'closed','t3'=>'closed','t4'=>'opened','t5'=>'opened');
foreach($gates as $gateKey=>$gateVal) {
$openGates->$gateKey = $gateVal;
}
echo $openGates->t1;
// or
foreach($gates as $key=>$val) {
echo $openGates->$key.PHP_EOL;
}
Btw you can simple set $openGates->gates = $gates; and call it like echo $openGates->gates['t1'];

PHP objecture structure chainability

I am trying to build a huge object structure that depends on hundreds of queries. To make it performant i did the following approach:
Each query result object is build into an object. For example a result set from table "apples" is build into an apple object. An orange result is build into an orange object.
Each object is saved in the registry, where the registry is a global array with functions like isExistent($type, $id), add($type, $obj) and get($type, $id). I want to store each object from point 1 once here to access it later without needing to call another query for the object (tons of objects are needed several times at different places).
I want objects to be chained together so i can do something like $tree->getFruits()->getApple()->getPlace()->getName(). I also want objects in my hierarchy to be linked in both directions for later use.
Why do i need this? For example the "apple" object with id 3 will be used once when the $tree object is instantiated, once when the $vitamins object is created, etc. I don't want to sent another query() each time to the database this object is needed. So i check something like:
Registry::isExistent("APPLE", $id)
and then get it from the Registry if it is there, and otherwise call it from db and then additionally add it to registry.
Problem: I am getting infinite loops for obvious reasons, which i could easily fix, but i don't know what's the best approach here.
Actual Code (not example stuff like apples and oranges from above):
$mask = new Mask($row["maskId"], $row["maskName"]);
foreach (Fields::getFieldsFor($mask->getId()) as $field) {
$mask->addField($field);
}
Registry::add("MASK", $mask);
and the getter for a field of a mask:
$mask = Registry::receive("MASK", $row["maskId"]);
$category = Registry::receive("FIELDTYPE", $row["fieldCategory"]);
$field = new Field($row["fieldId"], $category, $row["fieldQuestion"],
$row["fieldDescription"], $row["fieldPosition"], null, null, null, $mask);
Registry::add("FIELD", $field);
This will produce an infinite loop. A mask consists of several fields. When building the $mask object i want to have each field linked in the $mask array. When building each $field object i want to parent $mask linked to it. This will obviously fail because i only add to registry, AFTER they are done building which will obviously fail because before that they will already have a look at the registry.
There are 2 solutions i can think of:
Throw away my chainability and simply store the linked ids in my objects. So instead of storing a reference to the parent mask object in each field i would simply store the id and later retreive the actual object from the registry. This would be the easiest fix, but would throw away the chainability.
Split up the primitive object instantiation and the complex attributes. Just add the primitives via the constructor and add the complex like the actual $mask object to each field after the constructor AND after the Registry::add() via a setter.
What would be best to do here? Is something wrong in my general approach?
My goal simply is to build an easy to work with object structure (will be hundreds of objects) and to reduce redundant queries(). This really needs to be fast. I also only want each different object once and then linked accordingly in the complex object structure.
Thanks and sorry for a long description.
well, i've done something similar for one project few years ago using PHP Magic Method __get(). What i've done actually is storing the Getter Object inside a variable called Registry.get; something like: $this->get = new GetterObject($this);. Now, notice that i passed the Registry as a variable to my GetterObject, you might ask "why?" simply this is:
How it Works
The GetterObject gets initialized with the Registry as a variable (unless Registery as a global absolute object then no need for this!).
You "get" a ORM-object using something like Registery::get->fruits;.
The GetterObject will check the Registry array of objects to find fruits ORM-object.
The GetterObject will return a new instance of itself with some additional variables defining at which step you are; something like $this->path = "/fruits/";.
Now, when you "get" the second object, say, Registery::get->fruits->apples;, the GetterObject will simple check which was the last $this->path piece, on which it will depend to find the child element. Note that this step depends highly on how you structured your own ORM-objects array.
The GetterObject will repeat steps 4, and 5 until you end up with the object of desire and do something like ->return_values(); which is a defined function that returns the actual ORM-object you are looking for, not the GetterObject you were using to navigate through your ORM-objects.
As a small note again, this might not be the best practice for all the Registry-based platforms, so, make sure you really need something this complicated before doing so. Otherwise, a simple __get() definition might all what you need. Best of Luck.

php - multi-dimensional array wrapper class

I have some data represented as multi-dimensional arrays. I would like to embed these data into my OO application and provide extra functionality to these data but also a way to access it both with the traditional random access and using an iterator (so I could use foreach). In other words I would like to implement a wrapping class for a multi-dimensional array.
What I already tried:
I. ArrayAccess
The problem with this one is that the server uses PHP 5.2.17 and &offsetGet() gives an error, thus I can't return by reference, which means I can't implement multidimensional element access:
$obj[$key1][$key2] = $value;
Note, that upgrading to a newer PHP version is currently not an option
II. Using magic methods
This is a bit trickier, but my problems rose when I tried using a variable as key. E.g.
$obj->$key1[$key2] = $value;
The interpreter first evaluated $key1[$key2] throwing a notice and returning the first char of $key1, and uses that as key on my array. I don't know how to solve this one either. I thought of using brackets to force operation priority, but that had the same problem as in my next attempt:
III. Using simple get and set functions
Again, old PHP. It cries when I try to write:
$obj->get($key1)[$key2] = $value;
Because it doesn't know how to index an expression ending in round brackets.
There's still the lost resort option: make the array public and forget OO all together. But before I go there I want to be certain that there's just really no other way of doing this.
Sometimes, it's best shown with an example. For instance, you could have a multidimensional array with ordered quantities of a product, where the first index defines a customer, the second an order and the third the products. The value then is the quantity.
IV. Using simple get and set functions, but with multiple parameters:
$value = $obj->get($key1, $key2);
$obj->set($key1, $key2, $value);
or
$quantity = $orderedQuantities($customerName, $orderNo, $productCode);
The essence of writing an object wrapper, is that it looks like an object and behaves like such. If you implement it so that it looks and behaves like an array, you may just as well use an array. The only reason to wrap an array in an object that behaves like an array is that you can pass it around by reference, without using & parameters.
If that is your goal, just assign the array to $obj, and use $obj->arrayprop[$key1][$key2]. If on the other hand you want it to behave like an object, skip the array syntax altogether.
V. Using nested objects
So using the same example, you can write a customers object (kind of a collection), which embeds order objects, etc..
$quantity = $customers->getCustomerByName($customerName)->getOrder($orderNo)->getProduct($productCode)->quantity;
or, to make it more readable:
$customer = $customers->getCustomerByName($customerName);
$order = $customer->getOrder($orderNo);
$orderLine= $order->getOrderLine($productCode);
$quantity = $product->quantity;
Now, each of these entities are represented by an object. The advantage is that you can easily extend this structure with extra properties. You can give the customer collection 'findByCustomerNo' and 'findByName' methods to find a specific customer, but also a 'filterByPostalCode', which could return a new collection containing only the customers from that postal code.
$order could now contain functions to calculate totals.
$orderLine may contain only a productCode (key) and a quantity, but by making it an object, you can easily add discount, a customized name or description or other properties.
All in all, this structure allows a lot of flexibility, although it needs a little more typing at first and a little getting used to.
Using nested objects can make ArrayAccess work, without passing references. But you can forget about built in array functions completely.
<?php
class ArrObj implements ArrayAccess {
private $arr;
public function __construct($arr = null) {
$this->arr = $arr;
}
public function offsetExists($key) {
return isset($this->arr[$key]);
}
public function offsetGet($key) {
return $this->arr[$key];
}
public function offsetSet($key, $val) {
return $this->arr[$key] = $val;
}
public function offsetUnset($key) {
unset($this->arr[$key]);
}
}
$a = new ArrObj(array(
'foo' => new ArrObj(array(
'bar' => 'qwe'
))
));
echo $a['foo']['bar'] . '<br />';
$a['foo']['bar'] = 'asd';
echo $a['foo']['bar'] . '<br />';
?>
Outputs:
qwe
asd

Is there a version of the PHP array that is pass-by-reference?

I'm asking this because I'm working with a recursive function that generates a large array tree and the pass-by-copy aspect of the arrays are completely screwing with my head. I've tried using ArrayObject, but that's really an object, isn't it? None of the array_keys type array functions work with it, and json_encode doesn't understand that it's an array.
I'd like a version of the PHP array that feels, smells and looks like the normal array, but is pass-by-reference. Is there anything like that in PHP?
Woah woah hold up people; I'm well aware of the & symbol but that's what I'm trying to avoid. As my question specifies (^) I'm looking for a version of the PHP array that is pass-by-reference by default
I'd like a version of the PHP array that feels, smells and looks like
the normal array, but is pass-by-reference. Is there anything like
that in PHP?
No, There is nothing like that in PHP.
Json encode should be able to pass objects. But if you for some reason NEED an array, you can't use objects and then cast it as array before encoding to json?
<?php
$object = (object)array("number"=>1);
function addToTen($object){
if($object->number<10){
$object->number++;
addToTen($object);
}
}
addToTen($object);
echo json_encode((array)$object);
//echoes {"number":10} with or without casting it as an array
?>
You could also wrap your array in an object of course, like this:
$object = new stdClass;
$object->a = array();
function fillUpArray($object){
if(count($object->a)<10){
$object->a[] = "someValue";
fillUpArray($object);
}
}
fillUpArray($object);
echo json_encode($object->a);
//echoes ["someValue","someValue","someValue","someValue","someValue","someValue","someValue","someValue","someValue","someValue"]
I must admit though I don't entirely get what you're trying to accomplish here :S
Yes, see the PHP manual page: http://php.net/manual/en/language.references.pass.php
Stop using &references altogether, in php they get cumbersome pretty quickly, (being, unlike C pointers, almost transparent, the only way to check you're actually using a reference is by assigning junk to it and check the effect this has on a tree) and you don't seem willing to handle that level of subtlety.
(Nor to wrap it with an ArrayObject, apparently)
Are you aware objects ARE references?
Object-wrap every aspect of your tree and your life will instantly get less miserable.
I am not aware of any such built-in functionality in PHP that you ask. Also, you are quite reluctant to use references. Hmmm...you could send a request to the PHP dev team to include such stuff in PHP v6, along with unicode that is supposed to come, that would us all happy :).
However, can you use a class and assign your initial array to one of the class variables and then process it and get it back after the recursion. Not sure if that would work, but anyway here it is:
<?php
class noReference {
public $myData;
public function __construct( $data ) {
$this->myData = $data; // this is your initial array.
}
// this function works on the myData array and changes it.
public function myRecursiveFunction() {
// your code here
$this->myRecursiveFunction(); // called as per your logic
// your code here
}
public function getData() {
return $this->myData;
}
public function __destruct() {
unset( $this->myData );
}
}
$data = array(/*WHATEVER_PLEASES_YOU*/);
$noref = new noReference( $data );
// this will be your recuresive function
$noref->myRecursiveFunction();
//your data here
$result = $noref->getData();
?>
Let me know if this works. Cheers!
you can force php to pass things by reference by adding an &-sign to the parameter. read the documentation for more information.

Navigating array and objects data

I have an object that has a property link. I get it with getLink(). This property is actually an array so I get the 1st element $link[0] which in turn is an object so I get the property getHref(). My total code looks like this.
$link = $obj->getLink();
$link0 = $link[0];
$href = $link0->getHref();
Is there a better way to write this code, like in 1 line? something like $obj->getLink()->get(0)->getHref() I'm using PHP 5.3.4
current($obj->getLink())->getHref()
That's not very elegant though.
Your code should probably look like this:
$links = $obj->getLink();
$href = $links[0]->getHref();
If getLink always returns an array it should be called getLinks (plural) and I would expect to have to iterate over the return value; working only with the first result seems weird. Alternatively, getLink could accept a parameter to return a specific link, e.g.:
$href = $obj->getLink(0)->getHref();
PHP doesn't support direct return value dereferencing, so either the object interface is not very well suited for the language it's written in or you're not using the object as expected.

Categories