PHP Hamcrest matchers to assert partial array key/val pair content - php

I am testing my code with PHP and using Hamcrest matchers and I am wanting to assert that my returned array contains a particular key/value pair, however I am struggling.
For example, my array returns ['wanted-value' => 'some-value', 'arbitrary-value' => 'who-cares']
I want to assert that the returned array contains 'wanted-value' => 'some-value' but I don't care what else is in there.
I have seen arrayContaining used, but from what I can tell this only looks for a value or key, but not both?

Who would have thought the official docs would have the answer?
To be honest, I didn't know PHP docs for Hamcrest actually existed, I'd always just seen it as JAVA-specific.
The solution: hasKeyValuePair
assertThat($result, hasKeyValuePair('wanted-value', 'some-value'));

Related

Does PHP's in_array really go through the whole array?

I stumbled over a few articles (e.g. this one) and infos that suggest PHP's in_array() goes through the whole array.
Now there is a possible duplicate of this question here: How does PHP's in_array function work? but the OP was obviously satisfied with the copy/paste of the C language function definition and no further description...
My question however is:
Does PHP's in_array() really go through the whole array?
I tried to look further and go after the ZEND_HASH_FOREACH_KEY_VAL, but then it got a bit confusing:
the C-language definition of php_search_array() ... AKA in_arary() in PHP
Codes of ZEND_HASH_FOREACH_KEY_VAL and ZEND_HASH_FOREAC
Only thing I am sure of is that since the ??iteration?? happens on the "C-level" it should be faster than "manual" foreach...
Does PHP's in_array really go through the whole array?
TLDR; No it doesn't.
The way I read the C implementation:
ZEND_HASH_FOREACH_KEY_VAL or rather ZEND_HASH_FOREACH iterates over the array data bucket with a pointer to the current element.
The element pointer is assigned to the variable entry in void php_search_array for each iteration.
When a match is found, The PHP list item itself or PHP bool is returned by the engine depending on the behavior argument given to the function.
To answer your question:
php_search_array either invokes Zend RETURN_TRUE (impl: https://github.com/php/php-src/blob/master/Zend/zend_API.h) or sets RET_VAL and performs a C return; afterwards. It both cases, C execution breaks out of the iteration of the array if a match is found.

REALbasic array in an array

I am trying to write a soap parameter in REALbasic.
I need to add an array within another array similar to this in php:
$params = array(array(
'sku' => 'some sku'
));
so I can pass this:
$result = $client->call($session, 'catalog_product.list', $params);
I have
dim aArgs (0,1) as String
dim aParmas (0,1) as String
aArgs(0,0)="sku"
aArgs(0,1)="some sku"
aParmas(0,1)= aArgs
But receive a "Type mismatch error. Expected String, but got String(,)"
How can I do this.
Thanks
First off, the line
aParmas(0,1)= aArgs
is wrong because you assign an array (which is in aArgs) to a single element of aParmas. And since those single elements hold a String, you try to assign an array to a single string here, hence the error message.
But I think you're looking at this from the wrong end. You need to start with figuring out what parameters you need to send to the session function you want to call.
That means: You need to find the REALbasic function for $client->call. Once you know which function that is, look at the parameters that function expects. I doubt it expects a two-dimensional array for the "params". Once you know what to pass here, let us know if you still cannot figure out how to get it working.
An explanation of multidimensional arrays in REALbasic is here
The short answer is that you can't have a PHP-like array of arrays. You need to wrap your array in a class and make the class behave like an array.
Any reason you're using REALbasic? If it's cross-platform you're after, python is ALWAYS a better choice

Accessing specified Element of Array with Twig

I have the following array and i want to access the value of a specified element with twig.
numbers => Array ([01234567] => Array ( [0] => 9876543210 [1] => 8765432109 [2] => 0000000000))
I know there is only one entry in numbers, so I want to access the array with the key 01234567 directly.
Even tough numbers|keys[0] does return the correct key, I can't use it like numbers[numbers|keys[0]] to get the array. I also tried the attribute(array, item) function, but i didn't got it to work.
Is it possible to access it directly or do I need to use loops?
You have found a probably undocumented "feature" of Twig. If you check the source code, twig tries to determine if the given key is numeric, or not. It does this check with the ctype_digit function, which checks if a variable contains only numeric characters.
The example in your question contains an array key, which meets this conditions: it contains only numbers. Unfortunately, it also starts with a zero, which is removed when the string is converted into an integer.
I'm not exactly sure that this is intended behavior, so you may try to report this example as a bug.
For the current twig implementation, because everything except the loop construct uses the getAttribute function, you have no other choice but to use a for loop.

PHP SimpleXML #attributes behavior for nodelists

I've seen this problem a few times, and would like a definitive answer.
Given this structure in xml:
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
I am not able to access the attribute 'order'. var_dump (unsurprisingly) does not show any attributes for ByteListSet. Indeed, foreach iteration does not produce a #attributes item.
However, the following structure:
<ByteListSet>
<Byte Order="0"><Value>3729</Value></Byte>
</ByteListSet>
Results properly in ByteListSet having a child Byte that is a SimpleXmlObject which has #attributes.
I would assume that SimpleXML is indeed picking up the #attributes from the first case, but where is it keeping them? The trouble is that in the former structure, ByteListSet produces this on var_dump of ->children():
object(SimpleXMLElement)[25]
public 'Byte' => string '14' (length=2)
if I get_object_vars() on it and var_dump each, I simply get:
string '14' (length=2)
Indicating that Byte is not being returned to me as an xml object, but just as a string; as a property of the ByteList object above it.
Order="0" is there somewhere, but I don't have access to it. How do I get to it? NOTE: ->attributes() returns, as you would expect, a blank array.
(We do not control this schema, so it can't be restructured.)
You wrote:
Given this structure in xml:
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
I am not able to access the attribute 'order'.
Sure, because the attribute order does not exist. XML is case-sensitive, the attribute is named Order with uppercase O at the beginning.
Using the right attribute name allows you to access the value as outline in Example #5 in the basic examples of the SimpleXML extension you can find in the manual:
$ByteListSet = simplexml_load_string(<<<XML
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
XML
);
echo $ByteListSet->Byte['Order']; # 0
Please keep in mind that what you see in var_dump or print_r is not always what you get. This is espeically true for Simplexml (compare: How to get values of xml elements?; SimpleXML and print_r() - why is this empty?), however, also for other objects in PHP that make use of ArrayAccess, __toString() or IteratorIterator / IteratorAggregate.
Please let me know if that answers your question. You were not very specific which existing solutions you have not understood so far, so I answered your question, but I'm not 100% if I hit the nail.

Empty associative array SOAP type conversion

I have a client server scenario where the type conversion did by the SoapClient class in PHP, cannot tell wether an empty array is associative or numeric, and so defaults to numeric.
All exposed functions use basic types, no classes.
An associative array such as array("something"=>123) gets converted to a map data type. However, when the same array is empty, such as array(), it gets converted to an array on the Ruby side. Type casting to object (object)array() will result in a struct data type on the Ruby side.
The argument is a bit more complex, not as simple as above:
array(
"options"=>array(
"plans"=>array(
0=>array(
"name"=>"abc",
"product_options"=>array(
"optional_key_determines_associative_array_data_type"=>0,
),
),
),
),
);
If the array under "product_options" is empty, it gets converted to an array in Ruby, instead of a map. Once again, type casting to object in PHP results in a struct in Ruby.
What can I do on the PHP side to make empty "associative" arrays end up as maps on the Ruby side?
PHP 5.3.3, using SoapClient.
Ruby 1.8.7, Rails 2.3.2 using Action Web Service.
You can wrap your array in a SoapVar class with APACHE_MAP as encoding parameter. Like This:
array(
"options"=>array(
"plans"=>array(
0=>array(
"name"=>"abc",
"product_options"=> new SoapVar(array(), APACHE_MAP),
),
),
),
);
Well, this is exactly what I mean: To overcome that problem you will need to change the logic in your scripts - not the PHP. As you can not define a PHP array to be associative you will need to modify the receiving script.
If it was me I would not send an empty array. Put a status field into the array. This could be a field counting the available products which in this case would count 0. You will have a more meaningful communication AND the array IS suddenly associative no matter what
e.g
"product_options" => array ('products'=>0,'...'=>...) and so forth.
What I say is you will need to change the logic, you can not change PHP.
Hope that helps,
Uwe
I might be wrong here, but by my understanding:
I do believe what you are trying to achieve is not possible. An array (and we are talking array only, no objects) is an array. The structure given by the content makes an array associative or not.
An empty array is not associative.

Categories