How to "normalize" a php Object from SoapClient with inconsistent structure? - php

I have a php stdObject that is an object, and sometimes an array of Objects.
EDIT: It's been returned by SoapClient::__soapCall.
If it has one entry:
stdClass Object
(
[Event] => stdClass Object
(
[Nr] => 7050
[Date] => 2016-11-30T00:00:00
)
)
If it's got multiple entries:
stdClass Object
(
[Event] => Array
(
[0] => stdClass Object
(
[Nr] => 7015
[Date] => 2016-04-28T00:00:00
)
[1] => stdClass Object
(
[Nr] => 7016
[Date] => 2016-04-29T00:00:00
)
[2] => stdClass Object
(
[Nr] => 7017
[Date] => 2016-06-08T00:00:00
)
)
)
I don't see why, and I'd like to correct it.
Here's how I got there:
$items = $this->getAll(); // returns an Object with n entries
foreach($items as $item){
$fullItem = $this->item->getElementByID($item->Nr); // returns the full data for this entry
$item->Days = $fullItem->Days; // that's the one that can contain objects or just the data if it has one entry
}
My Questions:
Is that normal, that the same request returns different data types?
Should I "normalize" this and make an array of objects if there's only one entry?
Or is there an other recommended way to cope with it?

I don't think it's ok in this case. You should keep the interface the same. So if you are expecting an unknown number of elements it should always be a collection, even if in some cases only one element is returned. However if you expect only one element then the collection should be omitted.
$this->getAll(); //should always return a collection
$this->getOneById($id); //should return single item without collection, return null or throw exception if no element is found
On the question should you "normalize" (refactor) it I wold say it depends on the effort estimation and the benefit you'll gain form the refactoring. If this occurs in few places only and it's not likely to be used in any other place in the future I think you shouldn't bother. In case you decide to refactor be very careful and ensure the whole application is working properly. If you have tests - great.

I didn't declare it in the question, sorry: The data is the result of a soap client call:
$this->soapClient = new \SoapClient($wsdlUrl, array('trace' => DEBUG_SOAP));
// ...
$result = $this->soapClient->__soapCall($method, array($method => $params));
I think what confused me is the standard behaviour of SoapClient::__soapCall
SOAP functions may return one, or multiple values. If only one value
is returned by the SOAP function, the return value of __soapCall will
be a simple value (e.g. an integer, a string, etc). If multiple values
are returned, __soapCall will return an associative array of named
output parameters.
http://php.net/manual/en/soapclient.soapcall.php
Also, a very similar question: PHP SoapClient type mapping behaves differently
And here are two solutions from there:
"Normalize" (what's the correct word?) the data AFTER we got it:
if (is_object($variable)){
$variable = array($variable);
}
https://stackoverflow.com/a/6408780/160968
And, that would be the equivalent to refactoring the request BEFORE we get it – there's a setting for the soapClient!
$soapConfig = array(
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
'trace' => true
);
$client = new SoapClient($wsdlUrl, $soapConfig);
https://stackoverflow.com/a/8008715/160968
Awesome!

Related

PHP Arrays and stdClass objects

I am working on a project to get the names of an array.
The arrays seem to be multidimensional, with the added bonus of being a stdclass Object. I am trying to select a key from the provided array but seem to have no luck selecting them.
echo($response->array[shoecompany]->array[1]->name);
from the information here
stdClass Object
(
[shoe] => shoemaker
[shoecompany] => Array
(
[0] => stdClass Object
(
[shoenumber] => 1
[name] => Blank
[1] => stdClass Object
(
[shoenumber] => 2
[name] => demo
)
[2] => stdClass Object
(
[shoenumber] => certificate
[name] => certofsale
)
)
)
Nothing i do seems to pull the information i need out of this. Any ways to go about pulling, said information.
The arrays seem to be multidimensional, with the added bonus of being a stdclass Object.
Arrays and objects aren't the same things.
I let you learn more about the specifics of both if you are curious.
Regarding access, yous use brackets - '[]' - when you want to access something in an array and an arrow - '->' - when you want to access an object's property :
$array['key'];
$object->property;
In your case, since only $response and the entries in the entry showcompany - I assume it's a typo - are objects, what you should write is :
$response->shoecompany[1]->name;
Which gives you in practical use :
foreach ($response->shoecompany as $val) {
echo $val->shoenumber, ' : ', $val->name, '<br>'; // Or whatever you want to print, that's for the sake of providing an example
}
If it is more convenient for you to handle exclusively arrays, you can also use get_object_vars() to convert an object properties to an array :
$response = get_object_vars($response);
Code should be like:
echo $response->shoecomapny[1]->name;
In short, to select key inside an object you need to use "->" operator and to select key inside array use "[]".

Nested Array Foreach Issue

I am very basic new php learner, i having difficulty to get nested array value, here is my json result:
stdClass Object
(
[title] => Aao Raja - Gabbar Is Back | Chitrangada Singh
[link] => stdClass Object
(
[22] => Array
(
[0] => http://r8---sn-aigllnsk.c.docs.google.com/videoplayback?mime=video%2Fmp4&id=o-AExJcTxRDvCYsfgA1cIvQDs1v-pvLhKjTPdDh67X19vz&dur=145.542&itag=22&pl=48&ip=2a03:b0c0:1:d0::2f6:c001&sparams=dur,expire,id,ip,ipbits,itag,lmt,mime,mm,mn,ms,mv,nh,pl,ratebypass,source,upn&key=cms1&sver=3&expire=1437035009&upn=9lTw9Popb18&ratebypass=yes&source=youtube&lmt=1432539432699196&fexp=901816%2C9407809%2C9408142%2C9408420%2C9408710%2C9409172%2C9412774%2C9412846%2C9413149%2C9415664%2C9415958%2C9416126%2C9416370%2C9416656&ipbits=0&signature=3547894526817B37774A7838F8B68493CDD62101.3F143C74D76E8705800445A4CD4476C4F8BCD988&cms_redirect=yes&mm=31&mn=sn-aigllnsk&ms=au&mt=1437013301&mv=m&nh=IgpwcjAzLmxocjE0KgkxMjcuMC4wLjE&utmg=ytap1
[1] =>
[2] => hd720
)
[43] => Array
(
[0] => http://r8---sn-aigllnsk.c.docs.google.com/videoplayback?mime=video%2Fwebm&id=o-AExJcTxRDvCYsfgA1cIvQDs1v-pvLhKjTPdDh67X19vz&dur=0.000&itag=43&pl=48&ip=2a03:b0c0:1:d0::2f6:c001&sparams=dur,expire,id,ip,ipbits,itag,lmt,mime,mm,mn,ms,mv,nh,pl,ratebypass,source,upn&key=cms1&sver=3&expire=1437035009&upn=9lTw9Popb18&ratebypass=yes&source=youtube&lmt=1428933984759484&fexp=901816%2C9407809%2C9408142%2C9408420%2C9408710%2C9409172%2C9412774%2C9412846%2C9413149%2C9415664%2C9415958%2C9416126%2C9416370%2C9416656&ipbits=0&signature=266C126464ECDB4CC0FF076CD41F07BCC4DA7E34.08D9F13B7BF7D92FD1E1963336CC7FB8F19FE899&cms_redirect=yes&mm=31&mn=sn-aigllnsk&ms=au&mt=1437013301&mv=m&nh=IgpwcjAzLmxocjE0KgkxMjcuMC4wLjE&utmg=ytap1
[1] =>
[2] => medium
)
I can access the Title, but can't access the Link urls:
echo $title = $json->{'title'};
echo $link = $json->{'link'}->{'22'}->{'0'};
How can access the specific link array 22
This echo $title = $json->{'title'}; works because you are accessing an object's property and using -> is the correct way.
In this case $json->{'link'}->{'22'}->{'0'} you are trying to access an array item instead an object's property, because $json->{'link'}->{'22'} is an array and not an object. In this case, you should access it in this way: $json->{'link'}->{'22'}[0]. In order to avoid this kind of issues and, when you decode your JSON to a PHP object, you can pass true as a second parameter to the function json_decode and that will convert the whole object into an array. That way, you don't need to worry about accessing elements as object's attributes, you can access them, always, as array items. So, in this case, it would be: $json["link"]["22"][0].
You're confusing the way you access objects and arrays.
Getting the title is correct via $json->title, but the link should be $json->link->{'22'}[0] - a mixture of objects and arrays.
FYI the {'name'} notation is the same as name - only required when you are including variables in your object name e.g. {$someVar . 'name'}
I suppose you use json_decode() function. Do you know that you can get an array instead of StdClass Object? So, you can use.
<?php
$php_array = json_decode($json_string, true);

How to best add element to array at arbitrary index in PHP?

How can I write a solution for which the current PHP interpreter (5.4) is smart enough to simply do about 3-5 copies instead of a full on item-by-item array sort?
Note, I know a few methods to insert an element into an indexed array. However this does not satisfy my understanding. For instance in C++, you can do something using std::copy or make a struct or union as a multi-element array cursor.
So I wonder if I play by PHP's rules somehow, what syntax can one use to have, under the hood, something closer to
Copy the [range of elements from some index to the end of A] into temp C
Copy B into A[Index],
Copy C into A[Index+count(B)]
Than this...
$MasterItemList = $Page[$CurrentPage]->GetItems(); /* Returns an array with 512 Items. */
$UpdateList = GetUpdatePage(); /* Returns multi-dimensional array such that:
$result[][0]=an index and
$result[][1]=a list of items */
foreach($UpdateList as $Update)
{ foreach($Update as $cursor => $ItemList)
{
$cursor=$cursor+0; //to int..
$numitems=count($ItemList);
if($ItemList[0]->NewAddition)
{
$BeforeUpdate=array_splice($MasterItemList,0, $cursor, true);
$AfterUpdate=array_splice($MasterItemList, $cursor+$numitems, 0);
$MasterItemList=array_merge($BeforeUpdate,$ItemList,$AfterUpdate);
$Page[$CurrentPage]->OffsetCorrection+=$numitems;
}
else
{
$i=0;
foreach($ItemList as $LineItem)
{
$MasterItemList[$cursor+$i] = $LineItem;
$i++;
}
}
}
}
Forgive me if I've a few errors jotting this down, let me know and I'll correct them.
Namely though, I dont think proper referencing and scope are available to the interpreter for it to be able to do the logic directly using this method. It's already a woefully expensive looking thing.. What can be done to do this 'the right way' for PHP?
Examples:
// An Update List
Array(
[0] => Array(
[0] => 31
[1] => Array(
[1] => stdClass Object
(
[NewAddition] => false
[Name] => "********"
[Date] => 1364920943
[Active] => 1
.
.
.
)
[2] => stdClass Object
(
[NewAddition] => false
[Name] => "********"
[Date] => 1364920943
[Active] => 1
.
.
.
)
[3] => stdClass Object
(
[NewAddition] => false
[Name] => "********"
[Date] => 1364920943
[Active] => 1
.
.
.
)
)
)
)
And MasterItemList is simply an array of these same objects (class Item).
A few things to note:
This data is only accessed in a purely sequential manner anywhere it would matter for this script.
Only the first item in a newly inserted set needs to be checked for update in this part of the script. All items following in the set will be always be new.
Items trailing over 512 are auto-adjusted into the next page load. I can adjust size of pages to trade between array sorting performance & data fetch performance (async buffered).
First of all, PHP arrays are not "arrays" in the data structure sense; they are actually hash tables and doubly linked lists rolled into one. When you are indexing into an array e.g. with $list[$i] $i is hashed to find the corresponding element; it's not simple arithmetic as it is in e.g. C++.
Additionally, since arrays are also linked lists the implementation of array_splice is much more efficient than it might appear, at least if the portion being removed is small enough (hashing the new items is normally fast, and interposing items at a certain place of a linked list is constant time).
Of course this means that PHP arrays consume much more memory than a "pure" array would and they are also slower if all you are intending is index-based access. In those situations the SPL offers SplFixedArray which is an implementation of an array in the data structure sense of the word.
In your particular case, array_splice should be your first option; you can insert an array chunk with just one call:
array_splice($MasterItemList, $cursor, 0, $ItemList);

Getting a list of children from an array with Parents, without recursion in PHP

I have a situation where I have already obtained and manipulated SQL data into an array and into a tree. I am trying to avoid recursion at all costs because it has bitten me in the behind in the past.
I have an array of elements with Parent_IDs, and I'd like to be able to obtain a list of all of their children and subchildren. It shouldnt be nearly as complex as going the other way (from an array to a nested tree) which I got without issue using references, but for some reason I'm brain-frozen...
Any help appreciated.
I have the data in two possible formats because I have manipulated it already. Whichver is best for input can be used. this is a structure (print_r) of the two arrays I have:
Array
(
[202735] => Array
(
[ID] => 202735
[text] => aaafdf
[Parent] =>
)
[202737] => Array
(
[ID] => 202737
[text] => Filho 2
[Parent] => 202735
)
[202733] => Array
(
[ID] => 202733
[text] => Neto 1
[Parent] => 202731
)
[202739] => Array
(
[ID] => 202739
[text] => Neto 2
[Parent] => 202737
)
)
or
Array
(
[0] => Array
(
[ID] => 202735
[text] => aaafdf
[Parent] =>
[children] => Array
(
[0] => Array
(
[ID] => 202737
[text] => Filho 2
[Parent] => 202735
[children] => Array
(
[0] => Array
(
[ID] => 202739
[text] => Neto 2
[Parent] => 202737
)
)
)
)
)
[1] => Array
(
[ID] => 202733
[text] => Neto 1
[Parent] => 202731
)
)
Desired output in the format:
(first level parent => all children and grandchildren)
array(202731=>array(202735));
array(202735=>array(202737,202739));
or similar... Ideally i'll wrap this in a function like ListChildren($InitialParent), and return all children from such... invoking ListChildren(0) or (null) would list all elements and all subs...
(additional array elements can be ignored for the purpose of this exercise)
OBS: some data in the array is missing... namely the category above 202735, which would be 202731, but thats just because i limited the data I copied in... basically I can have either a flat array with parent ids, or a "tree" array with nested children as the source.
I ended up with this. Thanks for the help, in the end I reverted to a recursive code. I'll monitor it for performance although I suppose just within the PHP layer it wont be an issue. I had a bad experience when I went in for maintenance on a project that had DB calls in a recursive function... as the usage grew the recursvie function usage grew exponentially and the DB calls went to 100s of thousands...
anyways:
function __GetChildrenRec($Lista, $Categoria){
// Return false if $initialParent doesn't exist
if ($Categoria == 0) $Categoria = "";
if (!isset($Lista[$Categoria])) return FALSE;
// Loop data and assign children by reference
foreach ($Lista as $CategAtual) {
if ($CategAtual[Parent] == $Categoria) {
$Filhos[] = $CategAtual[ID];
$Filhos = array_merge((array)$Filhos,(array)self::__GetChildrenRec($Lista, $CategAtual[ID]));
}
}
// Return the data
return is_array($Filhos) ? $Filhos : array();
}
There really is no reason to avoid recursion unless it is causing you a genuine performance problem. Recursion is how most developers looking at problems of this nature would probably attempt to solve them, and using alternative approaches can hurt maintainability because your code is doing something that someone who needs to maintain your code at a later date doesn't expect.
Generally speaking, unrolling recursion means managing a stack. Recursion is a way of getting the language you're using to manage the stack for you, if you don't use recursion, then you'll need to manipulate a stack yourself inside your function. The simplest way of doing this is with array_push and array_pop, functions built into PHP that let you use an array as a stack.
The stack-based approach is rather complex compared to simply using recursion though, and if recursion is giving you problems then manually maintaining a stack certainly will. There are benefits to be sure, but honestly I'd suggest that you try to figure out recursion instead, as it really is easier to deal with, and while it isn't as performant as managing a stack yourself, the performance loss in probably not going to be a bottleneck in your code as the bottlenecks in PHP scripts tend to be where PHP interfaces with the outside world (databases, files, network connections, etc).
Using the first array format:
function list_children ($array, $initialParent) {
// Return false if $initialParent doesn't exist
if (!isset($array[$initialParent])) return FALSE;
// Loop data and assign children by reference
foreach ($array as &$item) {
if (isset($array[$item['parent']])) {
if (!isset($array[$item['parent']]['children'])) $array[$item['parent']]['children'] = array();
$array[$item['parent']]['children'][] = &$item;
}
}
// Return the data
return (isset($array[$initialParent]['children'])) ? $array[$initialParent]['children'] : array();
}
What this does is basically create the second array from the first, but it does it by reference - so the initial parent can still be found by it's ID, and returned. Returns the array of children, an empty array() if there are no children, or FALSE if the $initialParent doesn't exist.

How to access the members of this data in PHP?

Okay. Now I give up. I have been playing with this for hours.
I have a variable name $data.
The variable contains these contents: (extracted by using var_export())
array (
'headers' =>
array (
'content-type' => 'multipart/alternative; boundary="_689e1a7d-7a0a-442a-bd6c-a1fb1dc2993e_"',
),
'ctype_parameters' =>
array (
'boundary' => '_689e1a7d-7a0a-442a-bd6c-a1fb1dc2993e_',
),
'parts' =>
array (
0 =>
stdClass::__set_state(array(
'headers' =>
array (
'content-type' => 'text/plain; charset="iso-8859-1"',
'content-transfer-encoding' => 'quoted-printable',
),
'ctype_primary' => 'text',
)),
),
)
I removed some non-essential data.
I want to access the headers value (on the second line above) - simple: $data->headers
I want to access the headers value (on the fourteenth line after the stdClass:: stuff) - how?
How can I possibly access the values within the stdClass::__set_state section?
I tried var_export($data->parts); but all I get is
NULL
Is this variable declared the way you posted it? Like:
$data = array(
'headers' =>
array (
…
In that case, I'm not quite sure how you can access 'headers' via $data->headers. It should be $data['headers'], because it is an array, not an object.
Further down, stdClass::__set_state(array('headers' => …)) statically calls the method __set_state of the class stdClass. Whatever this method does I don't know, but only its return value will be assigned to the 'parts' => array(0 => ...) key.
If OTOH what you're showing is the result of var_dump($data), then this is incorrect nonsense, since stdClass::__set_state() would never show up in a var_dump. Something is fishy either in your code or in what you posted and it's hard to say without seeing more of it.
Disregard the above, var_export prints classes this funky way.
$data['headers'] should do it for the first headers part. Further down, $data['parts'][0]->headers should do the trick.
I ran into a similar problem with the open-source mail server Postal. In this case, I used the API to send an email message with two recipients. The API returns JSON, which I want to process to get the message ID Postal has assigned to each message. The JSON string decodes to a series of nested objects, according to print_r.
pObj: stdClass Object
(
[status] => success
[time] => 0.28
[flags] => stdClass Object
(
)
[data] => stdClass Object
(
[message_id] => a122e786-6c32-4085-b1ed-023e5923ca38#rp.postal.domain.com
[messages] => stdClass Object
(
[name1#domain1.com] => stdClass Object
(
[id] => 180
[token] => AblVpj6UW2wh
)
[name2#domain2.com] => stdClass Object
(
[id] => 181
[token] => io5wgEXO1boH
)
)
)
)
Looking at the structure, the first few layers are easy to extract. The trouble is that the messages object has attributes named for each recipient email address. Without knowing these address values a priori, I couldn't access the individual recipient objects programmatically to get at the message id.
I also examined this object using var_dump() and var_export(). That's where I saw this strange and misleading syntax: "stdClass::__set_state(array(". But there are no arrays at all in this object. In reality, this structure contains nothing but nested objects and attributes.
This situation was giving me fits until I asked the right question: what are the names of an object's attributes?
Now the programmatic solution is relatively easy because PHP has a function for that. I used get_object_vars() to get an array of attributes in the messages object. By simply looping on that array, I could then access the id and token attributes of each address. Here's the code, with a sample JSON response from Postal.
define('_EOL', "\n");
$json='{"status":"success","time":0.28,"flags":{},"data":{"message_id":"a122e786-6c32-4085-b1ed-023e5923ca38#rp.postal.domain.com","messages":{"name1#domain1.com":{"id":180,"token":"AblVpj6UW2wh"},"name2#domain2.com":{"id":181,"token":"io5wgEXO1boH"}}}}';
$pObj=json_decode($json);
$str='<pre>pObj: '.print_r($pObj, true)._EOL._EOL;
$str.=' - status='.$pObj->status._EOL;
$msgVars=get_object_vars($pObj->data->messages);
foreach ($msgVars as $varKey=>$msgObj)
{
$str.=' - '.$varKey.':'._EOL;
$str.=' - id='.$msgObj->id._EOL;
$str.=' - token='.$msgObj->token._EOL;
}
echo($str.'</pre>');
What made this hard is that the Postal design uses variable attribute names in the messages object. I would have made messages an array and put the email address into an attribute named address along with the attributes id and token. But it is what it is, and I hope this helps someone else.

Categories