<?php
/**
*
* Copyright (c) 2010, eBussola.com All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the eBussola.com.
* 4. Neither the name of the eBussola.com nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY EBUSSOLA.COM ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL EBUSSOLA.COM BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Leonardo Branco Shinagawa <hide@address.com>
*
*/
class Feedee implements IteratorAggregate
{
/**
* Service relative path
* @var string
*/
private $servicePath = 'Services/';
/**
* Where configs will be stored
* @var array
*/
private $config;
/**
* Constructor
*
* @param string $service
*/
public function __construct($service)
{
$this->service = ucfirst(strtolower($service));
}
/**
* This magic method helps to define configs to the service.
* Use set<Config Name> method.
*
* @param string $name
* @param mixed $value
* @return Feedee
*/
public function __call($name, $value)
{
$attr = strtolower(substr($name, 3));
$this->config[$attr] = $value[0];
return $this;
}
/**
* IteratorAggregate implementation
*
* @return Feedee_Service
*/
public function getIterator()
{
$className = "Feedee_{$this->service}";
require_once(dirname(__FILE__).'/'.$this->servicePath.$this->service.'.php');
return new $className($this->config);
}
/**
* Return the service case the service dont implements IteratorAggregate
*
* @return Feedee_Service
*/
public function getService()
{
return $this->getIterator();
}
/**
* Static method to access the feed url. It resolve itself the best
* http client (CURL or Zend_Http_Client) and store a cache response to
* speedup the next connection.
*
* The Cache is used only if you specify a cache path (it MUST be writable)
* Feedee_Cache::setCacheDir($path_to_cache);
*
* @param string $url
* @param integer $expireTime
* @return string
*/
public static function getHttp($url, $expireTime=7200)
{
$http = Feedee_HttpClient::factory();
if (Feedee_Cache::$cacheDir)
{
$name = md5($url);
$cache = new Feedee_Cache($name, $expireTime);
if ($cache->isExpired())
{
$data = $http->get($url);
$cache->write($data);
}
else
$data = $cache->get();
}
else
$data = $http->get($url);
return $data;
}
/**
* Static method to help parsing the XML to Array.
* Use it only when "simplexml_load_string" cant be used.
*
* @param string $xmlString
* @return array
*/
public static function xml2assoc($xmlString)
{
if (is_string($xmlString))
{
$xml = new XMLReader();
$xml->xml($xmlString);
}
else
$xml = $xmlString;
$tree = array();
while($xml->read())
switch ($xml->nodeType)
{
case XMLReader::END_ELEMENT:
return $tree;
case XMLReader::ELEMENT:
$node = array('tag' => $xml->name, 'value' => $xml->isEmptyElement ? '' : self::xml2assoc($xml));
if($xml->hasAttributes)
while($xml->moveToNextAttribute())
$node['attributes'][$xml->name] = $xml->value;
$tree[] = $node;
break;
case XMLReader::TEXT:
case XMLReader::CDATA:
$tree[] = $xml->value;
}
return $tree;
}
}
/**
* Each entry of the service must extend this Class.
*/
class Feedee_Entry
{
protected $attributes;
/**
* Magic method get attributes. If the attribute isn't setted, get<Attributre Name> is called.
*
* @param string $attr
* @return mixed
*/
public function __get($attr)
{
if (isset($this->attributes[$attr]))
return $this->attributes[$attr];
else
$this->attributes[$attr] = $this->{'get'.ucfirst($attr)}();
return $this->attributes[$attr];
}
/**
* Magic method to set attributes. If the attribute isn't setted, set<Attribute Name> is called.
*
* @param string $attr
* @param mixed $value
*/
public function __set($attr, $value)
{
if (!isset($this->attributes[$attr]))
if (method_exists($this, 'set'.ucfirst($attr)))
$this->attributes[$attr] = $this->{'set'.ucfirst($attr)};
else
$this->attributes[$attr] = $value;
else
throw new Exception('I Can´t change this atttribute, It´s already filled');
}
}
/**
* Class to cache http responses.
* You dont need to use it.
*/
class Feedee_Cache
{
public static $cacheDir = False;
protected $name;
protected $metadata;
protected $data;
protected $expireTime;
public function __construct($name, $expireTime=7200)
{
$this->name = $name;
$this->openFile();
$this->expireTime = $expireTime;
}
public static function setCacheDir($dir)
{
self::$cacheDir = $dir;
}
protected function openFile()
{
$filePath = self::$cacheDir.$this->name;
if (file_exists($filePath.'.cache'))
{
$this->data = file_get_contents($filePath.'.cache');
$this->metadata = unserialize(file_get_contents($filePath.'.metadata'));
}
}
protected function writeFile()
{
file_put_contents(self::$cacheDir.$this->name.'.cache', $this->data);
file_put_contents(self::$cacheDir.$this->name.'.metadata', serialize($this->metadata));
}
public function write($data)
{
$this->data = $data;
$this->metadata = array(
'name' => $this->name,
'expireIn' => strtotime("now + {$this->expireTime} seconds")
);
$this->writeFile();
}
public function get()
{
return $this->data;
}
public function isExpired()
{
if (is_null($this->data))
return True;
else
{
if (!is_null($this->metadata) && $this->metadata['expireIn'] < time())
return True;
else
return false;
}
}
}
/**
* Smart Http Client
* You dont need to use it.
*/
class Feedee_HttpClient
{
const CURL = 0;
const ZEND = 1;
protected $_client;
public function __construct($client)
{
$this->client = $client;
}
public static function factory()
{
if (function_exists('curl_init'))
return new Feedee_HttpClient(self::CURL);
elseif (class_exists('Zend_Http_Client'))
return new Feedee_HttpClient(self::ZEND);
else
throw new Exception('Not found neither CURL or ZEND Http Clients');
}
public function get($url)
{
switch ($this->client)
{
case self::CURL :
return $this->getCurl($url);
case self::ZEND :
return $this->getZend($url);
}
}
public function getCurl($url)
{
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, 0);
$data = curl_exec($curl);
curl_close($curl);
return $data;
}
public function getZend($url)
{
$client = new Zend_Http_Client($url);
$response = $client->request();
return $response->getBody();
}
}