PHP: array_map on object? - php

I'm trying to write a function that formats every (string) member/variable in an object, for example with a callback function. The variable names are unknown to me, so it must work with objects of all classes.
How can I achieve something similar to array_map or array_walk with objects?

use get_object_vars() to get an associative array of the members, and use the functions you mentioned.
btw, you can also do a foreach on an object like you would on an array, which is sometimes useful as well.

You can use get_object_vars(), but if you need more control, try using reflection. It's slower than get_object_vars() (or get_class_methods() for that matter), but it's much more powerful.

You are looking for get_object_vars / get_class_methods (the first gets the variables, the second the method names).

Related

PHP callback - array or arrow syntax?

Let's assume we have this function
<?php
function run($callback)
{
$callback($some, $params);
}
How to call it?
run($someObject->callback);
or rather
run([$someObject, 'callback']);
The first one seems better to me especially because of code suggestion but in documentation is used array notation.
Why is first one worse than second one?
The array notation is better because the arrow notation doesn't work. Functions in PHP aren't first class objects which can be passed around. Whenever you "pass a function", you must pass it by name; i.e. only its name. To pass an object method, you need to pass the object and the method name using the callable pseudo type notation.

How can I make ArrayIterator or ArrayObject work with implode?

I'm having a few problems with ArrayIterator (And, indeed, the same problem with ArrayObject).
For 99% of everything, my extended ArrayIterator behaves like an array and is working great.
Unfortunately, implode() does not like being given an ArrayIterator (or ArrayObject).
I can't spot in the docs anywhere which suggests any other classes to implement on by extended ArrayIterator, nor any other methods to override.
Can anyone suggest how to get this working? (Note: Casting to an array every time I use implode is not a solution, as I'd like this array-like object to work EXACTLY as an array, and not have the code using it to have to know/care/cast)
The easiest correct solution is to use iterator_to_array to feed implode, e.g.
$traversable = /* your iterator, ArrayObject or any other type of Traversable */
echo implode(",", iterator_to_array($traversable));
This will work as expected with anything that can be iterated with foreach.
try downcasting the array ((array) $arrayObject) : implode(",", (array) $arrayObject);

PHP: is this a bug: shuffle() expects parameter 1 to be array, object given?

I have an object that implements the ArrayableInterface (BTW, it's from Laravel's Eloquent ORM).
This object is $articles. So naturally, I can do this:
foreach ($articles as $article)
echo $article->title . "<br/>";
But I can't do this:
shuffle($articles);
I get the shuffle() expects parameter 1 to be array, object given warning.
No, it's not a bug.
PHP 5 allows you to use foreach() to loop through objects that aren't arrays. These objects are called Iterators.
Unfortunately, the old array-based functions, like shuffle() cannot process Iterators.
The main reason for this is that an Iterator may not even be sortable -- for example, you can have iterators that read directly from a file or a URL, and read a new line of data each time the foreach() loop cycles. This clearly can't be sorted because it's read during the foreach() process.
You can convert an Iterator into an array, using the cleverly named iterator_to_array() function. However, this may be a bad idea if you don't know how much data the iterator is going to process, as you may find it uses a lot of memory.
Some iterators may provide methods within the iterator object itself for sorting or filtering the data. If so, this is a better solution than trying to sort it as an array.
If you're working with an ORM, then this implies that your Iterator object is reading data from a DB. In this case, sorting it via the DB query (ie ORDER BY or whatever methods the ORM provides to do that) would probably be a better solution than sorting the data in PHP.
I don't know what the interface does.. but nor will the shuffle function because it only recognises arrays. You'd need to do this:
$array = iterator_to_array($articles);
$shuffled = shuffle($array);
From an OOP perspective, really, your object should contain the shuffle implementation:
$articles->shuffle();

array_values doesn't work with ArrayAccess object

array_values() doesn't work with ArrayAccess object.
neither does array_keys()
why?
if I can access $object['key'] I should be able to do all kind of array operations
No, you've misunderstood the utility of ArrayAccess. It isn't just a sort of wrapper for an array. Yes, the standard example for implementing it uses a private $array variable whose functionality is wrapped by the class, but that isn't a particularly useful one. Often, you may as well just use an array.
One good example of ArrayAccess is when the script doesn't know what variables are available.
As a fairly silly example, imagine an object that worked with a remote server. Resources on that server can be read, updated and deleted using an API across a network. A programmer decides they want to wrap that functionality with array-like syntax, so $foo['bar'] = 'foobar' sets the bar resource on that server to foobar and echo $foo['bar'] retrieves it. The script has no way of finding out what keys or values are present without trying all possible values.
So ArrayAccess allows the use of array syntax for setting, updating, retrieving or deleting from an object with array-like syntax: no more, no less.
Another interface, Countable, allows the use of count(). You could use both interfaces on the same class. Ideally, there would be more such interfaces, perhaps including those that can do array_values or array_keys, but currently they don't exist.
ArrayAccess is very limited. It does not allow the use of native array_ functions (no existing interface does).
If you need to do more array-like operations on your object, then you are essentially creating a collection. A collection should be manipulated by its methods.
So, create an object and extend ArrayObject. This implements IteratorAggregate, Traversable, ArrayAccess, Serializable and Countable.
If you need the keys, simply add an array_keys method:
public function array_keys($search_value = null, $strict = false)
{
return call_user_func_array('array_keys', array($this->getArrayCopy(), $search_value, $strict));
}
Then you can:
foreach ($object->array_keys() as $key) {
echo $object[$key];
}
The ArrayObject/ArrayAccess allows objects to work as arrays, but they're still objects. So instead of array_keys() (which work only on arrays) you should use get_object_vars(), for example:
var_dump(array_keys(get_object_vars($ArrObj)));
or convert your ArrayObject by casting it into array by (array) $ArrObj, e.g.:
var_dump(array_keys((array)$ArrObj));

php array or not array?

I don't understand... I'm doing something and when I do print_r($var); it tells me that I have an array, so naturally I think I have an array yet when I do
if(is_array($xml->searchResult->item))
it returns false
I use this array with foreach(); in documentation it says that foreach() won't work with anything else but array, so assuming that this is an array that I'm working...
plus, if I try to access it via
echo $xml->searchResult->item[3];
i will get 4th element of my array
print_r will also print objects as though they are arrays.
well, is_array() returns true if your variable is an array, otherwise, it returns false. In your case, $xml->searchResult->item seems not to be an array. What is the output for
var_dump($xml->searchResult->item)
? Another hint: You can determine the type of a variable via gettype().
is_array() returns true only for real php arrays. It is possible to create a "fake" array by using the ArrayAccess class. That is, you can use normal array semantics (such as item[3]) but it is not a real array. I suspect your $item is an object. So use
if($x instanceof ArrayAccess || is_array($x))
Instead.
plus, if I try to access it via echo $xml->searchResult->item[3]; i will get 4th element of my array
That's right, the first element is always 0 unless you specifically change it.
The manual does mention that foreach works on objects as well, and it will iterate over properties.
In your case, the situation is slightly different because I guess you're using SimpleXML, which is yet another special case. SimpleXMLElement has its own iterator, which I assume is hardcoded as it doesn't seem to implement any of SPL's Iterator interfaces.
Long story short, some objects can be used as an array, but they are not one.
Just be careful to check what has been returned by your array. You might have an object with class or stdClass which is empty and you cannot get element from it.

Categories