Reusing PHP function for accessing an array item tag from XML document
Pergunta
I have created a website that contains that uses XML to drive some of its contents, for example, the current exchange rates as shown below.
The website compares three exchange rates and I currently use a separate functions for each city that perform identical goals e.g. returning the current exchange rates.
The only difference between these functions are the positions of the item tag from the XML document that I request the data from.
$exchange['rate'] = $xml->channel->item[15]->description;
Notice item[15] in the above array which provides access to the Euro currency. In the following, the USA currency is item[56] as below.
$exchange['rate'] = $xml->channel->item[56]->description;
Any ideas if it is possible to combine the three functions into a single one to increase cohesion?
The function I use for accessing the Euro currency is:
<?php
function get_rate1(SimpleXMLElement $xml) {
// Match numeric values before and after decimal place
$exchange['rate'] = $xml->channel->item[15]->description;
preg_match('/([0-9]+\.[0-9]+)/', $exchange['rate'], $matches);
$rate = $matches[0];
// Get currency type from title
$title['rate'] = $xml->channel->item[15]->title;
$title = explode('/', $title['rate']);
$title = $title[0];
echo $rate . ' ' . $title . '<br />';
return $rate;
}
?>
The feed URL's are set in another configuration script called cityConfig.php
<?php
// City 1 //
$city1 = 'Paris';
$exchangeRate1 = '1 Euro';
$exchangeRate1RssUrl = 'http://themoneyconverter.com/rss-feed/EUR/rss.xml';
?>
Thanks in advance
Solução
How about you change the rate function to this:
<?php
function get_rate(SimpleXMLElement $xml, $id) {
// Match numeric values before and after decimal place
$exchange['rate'] = $xml->channel->item[$id]->description;
preg_match('/([0-9]+\.[0-9]+)/', $exchange['rate'], $matches);
$rate = $matches[0];
// Get currency type from title
$title['rate'] = $xml->channel->item[$id]->title;
$title = explode('/', $title['rate']);
$title = $title[0];
echo $rate . ' ' . $title . '<br />';
return $rate;
}
?>
Then you can call it like this instead:
$rate1 = get_rate($xml,15);
$rate2 = get_rate($xml,56);
Outras dicas
Try this on for size - it uses the three letter currency codes, and uses XPath to find them, doing away with those nasty indexes altogether. All you need to supply is the three letter code of the source currency, and the three letter code of the destination, or an array of destinations if you want to get more than one at once.
MODIFIED so that $dest
is now optional. If only $source
is supplied, return an array of all exchange rates.
function get_rate ($source, $dest = NULL) {
// Make sure source currency is upper case
$source = strtoupper($source);
// Construct the source URL
$url = "http://themoneyconverter.com/rss-feed/$source/rss.xml";
// This will hold the results
$result = array();
// Fetch and parse the data
if (!$xml = simplexml_load_file($url)) {
return FALSE;
}
if ($dest === NULL) {
// Get all <item> nodes and loop them
foreach ($xml->xpath("//item") as $item) {
// Find the code of this currency
$currencyParts = explode('/', $item->title);
// Get the value of this currency
$result[$currencyParts[0]] = (preg_match('/([0-9]+\.[0-9]+)/', $item->description, $matches)) ? (float) $matches[0] : FALSE;
}
} else {
// Loop the destination currencies
foreach ((array) $dest as $currency) {
// Make sure destination currencies are upper case
$currency = strtoupper($currency);
// Perform an XPath search for this currency
$nodes = $xml->xpath("//item[title='$currency/$source']/description");
// If we found the currency and could extract the value, add it to the
// result array as a float
$result[$currency] = (count($nodes) === 1 && preg_match('/([0-9]+\.[0-9]+)/', $nodes[0], $matches)) ? (float) $matches[0] : FALSE;
}
}
// If only a single value was requested return it, otherwise return the array
return (count($result) === 1) ? array_shift($result) : $result;
}
Example usage:
$result = get_rate('GBP', 'USD');
var_dump($result);
/*
float(1.58014)
*/
$result = get_rate('GBP', array('USD', 'EUR'));
var_dump($result);
/*
array(2) {
["USD"]=>
float(1.58014)
["EUR"]=>
float(1.20236)
}
*/
$result = get_rate('GBP');
var_dump($result);
/*
array(64) {
["AED"]=>
float(5.80445)
["ARS"]=>
float(6.85316)
["AUD"]=>
float(1.47589)
["BBD"]=>
float(3.16103)
["BHD"]=>
float(0.59427)
["BOB"]=>
float(10.92135)
["BRL"]=>
float(2.72171)
["CAD"]=>
float(1.57968)
["CHF"]=>
float(1.44883)
["CLP"]=>
float(759.35947)
["CNY"]=>
float(9.96753)
["COP"]=>
float(2840.97943)
["CZK"]=>
float(30.15863)
["DKK"]=>
float(8.97219)
["EGP"]=>
float(9.53446)
["EUR"]=>
float(1.20265)
["HKD"]=>
float(12.24901)
["HUF"]=>
float(350.91425)
["IDR"]=>
float(14121.92063)
["ILS"]=>
float(5.87877)
["INR"]=>
float(77.48491)
["ISK"]=>
float(194.46687)
["JMD"]=>
float(136.31954)
["JOD"]=>
float(1.12059)
["JPY"]=>
float(120.36272)
["KES"]=>
float(132.28924)
["KRW"]=>
float(1763.3828)
["KWD"]=>
float(0.43897)
["LBP"]=>
float(2382.62959)
["LKR"]=>
float(180.02093)
["LTL"]=>
float(4.1525)
["LVL"]=>
float(0.84522)
["MAD"]=>
float(13.39206)
["MXN"]=>
float(20.24582)
["MYR"]=>
float(4.77078)
["NAD"]=>
float(12.10631)
["NGN"]=>
float(253.27781)
["NOK"]=>
float(9.21948)
["NPR"]=>
float(123.97585)
["NZD"]=>
float(1.89597)
["OMR"]=>
float(0.6077)
["PAB"]=>
float(1.58052)
["PEN"]=>
float(4.25316)
["PHP"]=>
float(67.48803)
["PKR"]=>
float(142.95779)
["PLN"]=>
float(5.03909)
["QAR"]=>
float(5.75308)
["RON"]=>
float(5.23271)
["RUB"]=>
float(47.73085)
["SAR"]=>
float(5.92694)
["SEK"]=>
float(10.66422)
["SGD"]=>
float(1.96993)
["THB"]=>
float(48.79218)
["TRY"]=>
float(2.77931)
["TWD"]=>
float(46.6742)
["UAH"]=>
float(12.71293)
["USD"]=>
float(1.58052)
["UYU"]=>
float(30.74107)
["VEF"]=>
float(6.79622)
["VND"]=>
float(33119.73602)
["XAF"]=>
float(788.88394)
["XCD"]=>
float(4.2674)
["XOF"]=>
float(788.88394)
["ZAR"]=>
float(12.10631)
}
*/
Since I can't comment yet...helk's answer is perfect - you could take it one step further and define some class constants or define()'d constants so you aren't dealing with hard to remember numbers in your code (remember, you may not be the only person who ever looks at this code - you know what 15 is, but Jr. Dev Newguy does not):
<?php
define('CURRENCY_USA', 56);
define('CURRENCY_EURO', 15);
// OR something like:
class CurrencyHelper
{
const CURRENCY_USA = 56;
const CURRENCY_EURO = 15;
// of course, you could also define get_rate here too...
public function __construct(SimpleXmlElement $xml)
{
$this->xml = $xml;
}
public function get_rate($id)
{
// ... your code here
}
}
// Or, define your get_rate as originally intended:
function get_rate(SimpleXMLElement $xml, $id) {
// Match numeric values before and after decimal place
$exchange['rate'] = $xml->channel->item[$id]->description;
preg_match('/([0-9]+\.[0-9]+)/', $exchange['rate'], $matches);
$rate = $matches[0];
// Get currency type from title
$title['rate'] = $xml->channel->item[$id]->title;
$title = explode('/', $title['rate']);
$title = $title[0];
echo $rate . ' ' . $title . '<br />';
return $rate;
}
// Finally:
echo get_rate($xml, CURRENCY_EURO); // define
echo get_rate($xml, CurrencyHelper::CURRENCY_USA; // class constant
$myCurr = new CurrencyHelper($xml);
$myCurr->get_rate(CurrencyHelper::CURRENCY_USA); // Over-oop'd solution : P
?>