No I took what you posted entirely and tried it… it didn’t work … so either I am sure I probably did something wrong…
[php]
if ($xml->gms->g[ $i ][‘h’] == “TBD” and $xml->gms->g[ $i ][‘v’] == “TBD”) {
echo “”;
}
elseif ($xml->gms->g[ $i ][‘v’]== “TBD” ){
echo “
{$xml->gms->g[ $i ][‘gt’]}
”;echo “
”. $home ." {$xml->gms->g[ $i ][‘hs’]}
";echo “
TBD {$xml->gms->g[ $i ][‘vs’]}
”;echo “
”;
}
else {
echo “
{$xml->gms->g[ $i ][‘gt’]}
”;echo “
” . $home . " {$xml->gms->g[ $i ][‘hs’]}
";echo “
” . $visit . " {$xml->gms->g[ $i ][‘vs’]}
";if ($time == “F”) {
echo " Final “;
echo “
”;
}
else if ($time == “P”) {
echo " {$xml->gms->g[ $i ][‘d’]}”;
echo “
”;
}
else {
echo " {$xml->gms->g[ $i ][‘q’]}";
echo “
”;
}
}}
[/php]
This is doing exactly what I want it to do! Shows all the info including TBD and works for both regular and post season!
POSTSEASON:
DIV
Patriots 0
TBD 0
WC
Texans 0
Chiefs 30
Final
WC
Vikings 0
Seahawks 0
Sun
REGULAR SEASON:
REG
Falcons 17
Saints 20
Final
One code covers all of it... :)
Now for me let me see if I have this right in my head…
[php]$i = 0; $i < count ( $xml->gms->g ); $i ++[/php]
first $i=0 means starts at the beginning then $i gets the count from the contents of $xml->gms->g; $i++ goes until the end of the contents?
Is that correct?
Pretty much. Glad you got it going.
I really do appreciate all the help!! I learned a new way to parse… I like it. Learning this stuff amazes me and I know I tend to over think things… but this cut 75 lines out of my original code!! WOW and has it sped it up!!
Remember that you should cache the data you receive from nfl.com. It’s good convention to not hit APIs needlessly, and it will (again) drastically reduce your execution time.
I ended up playing a bit around with the code, without going too crazy. With my changes the normal page load (without cache) was 170-250ms on my dev environment (virtual machine). With cache that dropped to 8-15ms.
FileCache.php
[php]<?php
class FileCache {
private $path;
public function __construct($path) {
if (!file_exists($path)) {
throw new Exception('Directory does not exist');
}
if (is_file($path)) {
throw new Exception('Given path argument is a file, not a directory');
}
if (!is_writable($path)) {
throw new Exception('Directory is not writable');
}
$this->path = $path;
}
public function save($key, $data, $expiry = 3600) {
$file = $this->path . DIRECTORY_SEPARATOR . $this->hashKey($key);
file_put_contents($file, json_encode(array(
'expires' => time() + $expiry,
'data' => $data
)));
}
public function get($key) {
$file = $this->path . DIRECTORY_SEPARATOR . $this->hashKey($key);
if (!file_exists($file)) {
return null;
}
$cache = json_decode(file_get_contents($file));
if (!isset($cache->expires) || $cache->expires < time()) {
unlink($file);
return null;
}
return $cache->data;
}
private function hashKey($key) {
return hash('sha1', $key);
}
}
[/php]
NflApi.php
[php]<?php
final class NflApi {
const URL = 'http://www.nfl.com';
private $cache;
public function __construct(FileCache $cache) {
$this->cache = $cache;
}
/**
* @return array of games
*/
public function getScoreStripGames() {
$path = '/liveupdate/scorestrip/postseason/ss.xml';
$cached = $this->cache->get($path);
if ($cached) {
return $cached;
}
$xmlData = $this->get($path);
if (!isset($xmlData->gms) || !isset($xmlData->gms->g)) {
throw new Exception('Invalid response');
}
$games = $this->parseGames($xmlData->gms->g);
$this->cache->save($path, $games);
return $games;
}
/**
* Get data from the server
* @param string $path
* @return SimpleXMLElement an object of class SimpleXMLElement with
* properties containing the data held within the XML document, or <b>FALSE</b> on failure.
*/
private function get($path) {
$xmlData = simplexml_load_file(self::URL . $path);
if (!$xmlData) {
throw new Exception('Invalid response');
}
return $xmlData;
}
/**
* Normalize XML data to game data
* @param SimpleXMLElement $xmlGames
* @return array
*/
private function parseGames(SimpleXMLElement $xmlGames) {
$games = array();
foreach ($xmlGames as $xmlGame) {
$game = array(
'date' => (string) $xmlGame['d'],
'teams' => array(
'home' => array(
'abbr' => (string) $xmlGame['h'],
'name' => (string) $xmlGame['htn'],
'score' => (string) $xmlGame['hs'],
'shortName' => (string) $xmlGame['hnn']
),
'visit' => array(
'abbr' => (string) $xmlGame['v'],
'name' => (string) $xmlGame['vtn'],
'score' => (string) $xmlGame['vs'],
'shortName' => (string) $xmlGame['vnn']
),
),
'status' => (string) $xmlGame['q'],
'time' => (string) $xmlGame['t'],
'type' => (string) $xmlGame['gt']
);
$games[] = $game;
}
return $this->prepareOutput($games);
}
/**
* Converts output data so we get clean arrays with objects.
* @param mixed $data
* @return mixed
*/
private function prepareOutput($data) {
return json_decode(json_encode($data));
}
}[/php]
Controller/view
[php]<?php
require ‘FileCache.php’;
require ‘NflApi.php’;
$cachePath = DIR . DIRECTORY_SEPARATOR . ‘cache’;
$cache = new Filecache($cachePath);
$api = new NflApi($cache);
$games = $api->getScoreStripGames();
foreach ($games as $game) { ?>
<p><?= $game->type ?></p>
<?php foreach ($game->teams as $team) { ?>
<p><?= $team->abbr == 'TBD' ? 'TBD' : ucfirst($team->shortName) . ' ' . $team->score ?></p>
<?php } ?>
<span class="danger">
<?php if ($game->status === 'F') { ?>
Final
<?php } else if ($game->status === 'P') { ?>
<?= $game->date ?>
<?php } else { ?>
<?= $game->status ?>
<?php } ?>
</span>
<hr>
<?php
}[/php]
This will require a cache folder where your web server has read/write permissions. You can change folder path/name in the controller/view file above.
[hr]
There are still tons of improvements that could be made for this.
If you're able to use some sort of in memory cache that would be better than file cache (though your server should cache files as well).
I'm not sure how you require files, but autoloading would be nice (instead of manually adding require all over).
Because of this uncertainty I didn't go nuts with classes (would be too much of a hassle to require everything). But there are lots to do there for improvements. Like adding a CacheDriver interface that would let you implement FileCache, MemcachedCache, etc with keeping a "contract" so your app is sure to handle the different types.
[hr]
I just wanted to pitch in another way to look at this. As you can see your original php file is now _very_ simple. It doesn't really take care of much anything, other than asking for some data and showing it. This file would in most frameworks be split into two files (hence the controller/view name). With the view portion starting at line 12 (the foreach).
You should be able to improve the Api class by adding additional methods like "getScoreStripGames" to get other sets of data.
If you need additional fields for each game you could add that in the parseGames method. Note that it might make sense to have different parsers for different types of data sets (another request might serve different data, you might only need a specific set of fields, etc)
WOW Thanks Jim… that’s quite a spin on things and I’m using Joomla that has built in cache for modules which is what this is… BUT I can always put a directory in my module path and it would be writable so that would work and probably better!! Turn off cache in joomla for that module and use this method as I’m sure since it’s specific it would work better!
I will play around with it probably tomorrow [NFL playoff game are on now LOL]…
Thanks everyone!
John
Jim I really like this and have looked it over…it makes sense to me… I did try running it and I get the infamous 500 error so I’m not really sure where to even begin with this LOL…
Well, step one would be to turn on error reporting and see which error(s) you get
[php]<?php
// add this to the top of your file (with the controller/view code)
error_reporting(E_ALL);
ini_set(“display_errors”, 1);
// rest of code[/php]
Note that the code I added throws exceptions, like if you point the file cache to a dir/file that isn’t valid. These exceptions are not handled by the code I pasted though, so you could add a try/catch block around the controller-part of the controller/view file to catch exceptions thrown in that code (including the code it calls).
Thank you Jim!
Yeah the error was mine … copy it correctly and it’ll work…
The only other error was I made the cache dir but didn’t make it writable… so once I changed that BINGO!
Now I will play with it and figure out how to make them show in the proper sequence… It’s taking the xml verbatim and showing the results… like this:
WC
WC
DIV
DIV
CON
CON
DIV
DIV
WC
WC
PRO
SB
Proper would be:
WC
WC
WC
WC
DIV
DIV
DIV
DIV
CON
CON
PRO
SB
I will have to figure that out…
Oh and you’re right… this way loads very quick… WOW.
You could go a few ways, load the data into an array or create a class that the data fills. Then, you sort it before output.