KISS is always the best approach:
foreach($page->paragraphs as $paragraph)
foreach($paragraph->image_ids as $image_id)
$image = $page->images[$image_id]
But if you wanted something a little more special a delegate can assist in providing the "missing" functionality you require and for which php 5.3 has made things super easy by means of a little magic.
<?php
class PageDelegate
{
private $page;
public function construct($page)
{
$this->page = $page;
}
/* The magic delegating methods __call, __set and __get */
public function __call($name, $args)
{
if (method_exists($this->page, $name))
return call_user_func_array(array($this->page, $name), $args);
// you may require some conversion to valid method name
// like converting - to _ etc. this should be kept in mind.
if (array_has_key($this->images, $name))
return $this->images[$name]; // note how this becomes $this->page->images[$name]
throw BadMethodCallException("Method $name does not exist");
}
public function __get($name)
{
if (property_exists($this->page, $name))
return $this->page->{$name};
throw InvalidArgumentException("Property $name does not exist");
}
public function __set($name, $value)
{
if (property_exists($this->page, $name))
$this->page->{$name} = $value;
else
throw InvalidArgumentException("Property $name does not exist");
}
// And perhaps something to help out with common tasks
public function images_in_paragraph($paragraph)
{
$images = array();
foreach($paragraph->image_ids as $image_id)
// Note how this becomes $this-page->images[$image_id];
$images[] = $this->{$image_id}();
// return the collection of image objects in the paragraph
return $images;
}
}
Using just the 3 magic methods __call, __set and __get we are effectively delegating to all instance members of Page and you can now call for images using their image id as a method. If there are any Page functionality you want to overwrite, simply adding the function would see it being called instead of our magic __call method to do your bidding. Now it doesn't matter that you can't change their source, you can still do what you please. =)
The implementation would then look something like this perhaps
$page = new PageDelegate($page);
foreach($page->paragraphs as $paragraph)
foreach($page->images_in_paragraph($paragraph) as $image)
echo "<img title=\"$image->title\" src=\"$image->src\">";
nJoy!