false, "debug" => false, "nocache" => false, "max_download_tries" => 3, "trace" => false ); function __construct($url, $parserType = COREYLIB_PARSER_XML) { if (clAPI::$options['debug'] || clAPI::$options['display_errors']) { error_reporting(E_ALL); ini_set('display_errors', true); } if (!empty($url)) { $this->url = $url; if (!$parserType) { // attempt autodetection if (preg_match('/(xml|rss)$/', $url)) $parserType = COREYLIB_PARSER_XML; else if (preg_match('/(json)$/', $url)) $parserType = COREYLIB_PARSER_JSON; else self::error("Please specify a parser type for $url - parameter two of the constructor should be one of COREYLIB_PARSER_XML or COREYLIB_PARSER_JSON."); } $this->parserType = $parserType; } else self::error("Um... you have to tell me what URL you want to parse."); } function __toString() { return ($this->content ? $this->content : ''); } static function configure($option_name_or_array, $value_or_null = null) { if (is_array($option_name_or_array)) self::$options = array_merge(self::$options, $option_name_or_array); else self::$options[$option_name_or_array] = $value_or_null; } static function error($msg) { if (clAPI::$options['debug'] || clAPI::$options['display_errors']) { ?>
'.$source;
$node = $this->get($path, $limit);
if (is_array($node)) {
foreach($node as $n)
self::blockquote($n->__name, $n);
}
else if (is_object($node))
self::blockquote($node->__name, $node, ($source));
else if ($node !== null)
echo "$path: $node";
if ($source) echo '';
return '';
}
private static function blockquote($name, $node, $hide = true) {
if (is_object($node)) {
$attributes = array();
foreach($node->__attributes as $n => $v)
$attributes[] = "$n="".htmlentities($v).""";
echo '';
}
else if (is_array($node)) {
foreach($node as $instance)
self::blockquote($name, $instance);
}
}
function __get($name) {
$sxml = $this->__value;
return $sxml->$name;
}
function __toString() {
ob_start();
echo $this->__value;
$content = ob_get_clean();
return ($content !== null ? $content : '');
}
function renderTwitterLink($anchorText = '»') {
$text = $this->get('text');
$text = preg_replace('#http://[^ ]+#i', '\\0', $text);
$text = preg_replace('/@([a-z0-9_]+)/i', '\\0', $text);
return $text.' '.$anchorText.'';
}
function rewind() {
$this->__position = 0;
}
function current() {
if ($this->__position == 0)
return $this;
}
function key() {
return 0;
}
function next() {
++$this->__position;
}
function valid() {
return ($this->__position == 0 && $this->__value);
}
}
/**
* Cache whatever, dude.
* @package coreylib
* @version 1.0.0
*/
class clCache {
private static $currentNameQueue = array();
private static $mysqlTableExists = null;
private static $mysqlConnection;
private static $inMem = array();
public static $options = array(
'nocreate' => false,
'mysql_host' => '',
'mysql_database' => '',
'mysql_username' => '',
'mysql_password' => '',
'mysql_table_prefix' => 'coreylib_'
);
static function configure($option_name_or_array, $value_or_null = null) {
if (is_array($option_name_or_array))
self::$options = array_merge(self::$options, $option_name_or_array);
else
self::$options[$option_name_or_array] = $value_or_null;
}
/**
* Prepare the local filesystem for caching our stuff.
* @param $name The unique identifier from which to generate a unique cache file
* @returns mixed When initialization fails, returns false; otherwise, returns an array with three parameters: the new file name, the path to the cache folder, and the path to the cache file.
*/
private static function initFileSystem($name) {
$fileName = md5($name);
$cachePath = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR.'.coreycache';
if (!file_exists($cachePath)) {
if (@mkdir($cachePath) === false) {
clAPI::error("Failed to create cache folder $cachePath");
return false;
}
}
$pathToFile = $cachePath.DIRECTORY_SEPARATOR.$fileName;
return array($fileName, $cachePath, $pathToFile);
}
/**
* Retrieves the value of the first column of the first row in the query result.
* @return When now rows are returned by the query, null; otherwise, returns the first value.
*/
private static function getVar($query) {
if ($result = @mysql_query($query, self::$mysqlConnection)) {
$arr = mysql_fetch_array($result);
return $arr[0];
}
else
return null;
}
/**
* @return The coreylib cache table name, with the proper prefix appended.
*/
private static function cacheTableName() {
return (isset(self::$options['mysql_table_prefix']) ? self::$options['mysql_table_prefix'] : '')."cache";
}
/**
* Initialize MySQL caching: connect to the database and create the cache table if needed.
* @return When initialization succeeds, true; otherwise, false.
*/
private static function initMySql() {
if (self::$mysqlTableExists === null) {
clAPI::trace('Initializing MySQL cache.');
if (!(self::$mysqlConnection = mysql_connect(self::$options['mysql_host'], self::$options['mysql_username'], self::$options['mysql_password']))) {
clAPI::error('Failed to connect to the MySQL server.');
return false;
}
if (!@mysql_select_db(self::$options['mysql_database'], self::$mysqlConnection)) {
clAPI::error('Unable to select database `'.self::$options['mysql_database'].'`');
return false;
}
if (!self::$options['nocreate']) {
self::$mysqlTableExists = self::cacheTableName() == self::getVar("SHOW TABLES LIKE '".self::cacheTableName()."'");
if (!self::$mysqlTableExists) {
clAPI::trace('Creating cache table '.self::cacheTableName().'');
if (!@mysql_query('CREATE TABLE `'.self::cacheTableName().'` (`id` VARCHAR(32) NOT NULL PRIMARY KEY, `cached_on` DATETIME NOT NULL, `content` LONGBLOB)', self::$mysqlConnection)) {
clAPI::error('Failed to create cache table `'.self::cacheTableName().'`: '.mysql_error(self::$mysqlConnection));
return false;
}
else
self::$mysqlTableExists = true;
}
else
return true;
}
else {
self::$mysqlTableExists = true;
clAPI::warn("You didn't let coreylib check to see if the cache table was there. Hope you're right.");
return true;
}
}
else
return self::$mysqlTableExists;
}
/**
* Parses $cacheFor into a timestamp, representing a date in the past.
* @param $cache A value that strtotime() understand
* @return a Unix timestamp, or false when unable to parse $cacheFor
*/
private static function parseCacheFor($cacheFor) {
if (!is_numeric($cacheFor)) {
$original = trim($cacheFor);
$firstChar = substr($cacheFor, 0, 1);
if ($firstChar == "+")
$cacheFor = '-'.substr($cacheFor, 1);
else if ($firstChar != "-") {
if (stripos($cacheFor, 'last') === false)
$cacheFor = '-'.$cacheFor;
}
if (($cacheFor = strtotime($cacheFor)) === false) {
clAPI::error("I don't understand $original as an expression of time.");
return false;
}
return $cacheFor;
}
else {
$cacheFor = time()-$cacheFor;
return $cacheFor;
}
}
private static function getFileCacheUnlessIsOldOrDoesNotExist($name, $cacheFor) {
if (!$cacheFor)
return false;
if (!($cacheFor = self::parseCacheFor($cacheFor)))
return false;
$init = self::initFileSystem($name);
if ($init == false)
return false;
else
list($fileName, $cachePath, $pathToFile) = $init;
if (isset(self::$inMem[$fileName])) {
clAPI::debug("File cache $fileName found in memory! Now that's fast.");
return self::$inMem[$fileName];
}
if (!file_exists($pathToFile)) { // file does not exist
clAPI::debug("File cache $fileName does not exist.");
return false;
}
if (($fileAge = @filemtime($pathToFile)) === false) { // couldn't read the file last-modified time: have no idea how old the file is!
clAPI::error("Unable to read file modification time of $pathToFile");
return false;
}
$content = ($cacheFor < $fileAge) ? @file_get_contents($pathToFile) : false;
if ($content === false)
clAPI::debug("File cache $fileName was too old.");
else
self::$inMem[$fileName] = $content;
return $content;
}
private static function getMysqlCacheUnlessIsOldOrDoesNotExist($name, $cacheFor) {
if (!$cacheFor)
return false;
$md5 = md5($name);
if (isset(self::$inMem[$md5])) {
clAPI::debug("MySQL cache $md5 found in memory! Now that's fast.");
return self::$inMem[$md5];
}
if (self::initMySql()) {
$cacheFor = date('Y/m/d H:i:s', self::parseCacheFor($cacheFor));
clAPI::debug("Querying MySQL for cached content named $md5 cached no earlier than $cacheFor.");
$content = self::getVar("SELECT content FROM `".self::cacheTableName()."` WHERE id='$md5' AND '$cacheFor' < cached_on");
if ($error = mysql_error(self::$mysqlConnection)) {
clAPI::error('Failed to query the database for cached content. See error log for details');
error_log('Failed to query the database for cached content: '.$error);
return false;
}
else if ($content === null) {
clAPI::debug("No MySQL cached content found for $md5");
return false;
}
else {
clAPI::debug("MySQL cached content found for $md5. Yippie!");
self::$inMem[$md5] = $content;
return $content;
}
}
else
return false;
}
private static function getCacheUnlessIsOldOrDoesNotExist($name, $cacheFor) {
return (self::isMysqlMode() ? self::getMysqlCacheUnlessIsOldOrDoesNotExist($name, $cacheFor) : self::getFileCacheUnlessIsOldOrDoesNotExist($name, $cacheFor));
}
/**
* If cached data exists for $name within $cacheFor, if $return is true, return the cached data, otherwise print it to the output buffer.
* @param $name String The unique name underwhich the cached data is expected to be stored
* @param $cacheFor String A string-representation of a point in the past, best expressed in terms of minutes, hours, days, weeks, or months, e.g., "1 minute" or "2 days" - anything that strtotime() can understand.
* @param $return boolean When true, if cached data is found, that cached data is returned by the function instead of being printed to the output buffer
* @return When no data is cached under $name or when cached data is older than $cacheFor, returns false; otherwise, returns data according to the value of $return.
*/
static function cached($name, $cacheFor = 0, $return = false) {
clAPI::trace("Looking for cached content $name no older than $cacheFor.");
if (empty($name))
return false;
if (($content = self::getCacheUnlessIsOldOrDoesNotExist($name, $cacheFor)) === false) {
if (!$return) {
self::$currentNameQueue[] = $name;
ob_start();
}
return false;
}
else {
if ($return)
return $content;
else {
echo $content;
return true;
}
}
}
static function flush($name) {
if (self::isMysqlMode()) {
if (!self::initMySql())
return false;
else {
$md5 = md5($name);
if (!@mysql_query("DELETE FROM `".self::cacheTableName()."` WHERE id='$md5'", self::$mysqlConnection) === false && $error = mysql_error(self::$mysqlConnection)) {
clAPI::error("Failed to flush cache $md5. See server error log for details.");
error_log("Failed to flush cache [$md5]: $error");
return false;
}
else
return true;
}
}
else {
if (!($init = self::initFileSystem($name)))
return false;
else
list($fileName, $cachePath, $pathToFile) = $init;
if (file_exists($pathToFile) && !@unlink($pathToFile)) {
clAPI::error("Failed to delete cache file for $name.");
return false;
}
else
return true;
}
}
public static function saveContent($name, $content, $cacheFor = 0) {
if (self::isMysqlMode()) { // mysql cache
if (!self::initMySql())
return false;
else {
$md5 = md5($name);
if (!@mysql_query("REPLACE INTO `".self::cacheTableName()."` (id, cached_on, content) VALUES ('$md5', NOW(), '".mysql_escape_string($content)."')", self::$mysqlConnection) && $error = mysql_error(self::$mysqlConnection)) {
clAPI::error("Failed to cache $md5. See server error log for details.");
error_log("Failed to cache [$md5]: $error");
return false;
}
else
return true;
}
}
else { // file system cache
if (!($init = self::initFileSystem($name)))
return false;
else
list($fileName, $cachePath, $pathToFile) = $init;
if (@file_put_contents($pathToFile, $content) === false) {
clAPI::error("Failed to save cache file $pathToFile.");
return false;
}
else
return true;
}
}
public static function save() {
$content = ob_get_flush();
self::saveContent(array_pop(self::$currentNameQueue), $content, 0);
}
private static function isMysqlMode() {
return (
@strlen(self::$options['mysql_host'])
&& @strlen(self::$options['mysql_database'])
&& @strlen(self::$options['mysql_username'])
&& @strlen(self::$options['mysql_password'])
);
}
}
if (!function_exists('cached')) {
/**
* If cached data exists for $name within $cacheFor: if $return is true, return the cached data, otherwise print it to the output buffer.
* @param $name String The unique name underwhich the cached data is expected to be stored
* @param $cacheFor String A string-representation of a point in the past, best expressed in terms of minutes, hours, days, weeks, or months, e.g., "1 minute" or "2 days" - anything that strtotime() can understand.
* @param $return boolean When true, if cached data is found, that cached data is returned by the function instead of being printed to the output buffer
* @return When no data is cached under $name or when cached data is older than $cacheFor, returns false; otherwise, returns data according to the value of $return.
*/
function cached($name, $cacheFor = 0, $return = false) {
return clCache::cached($name, $cacheFor, $return);
}
}
if (!function_exists('save')) {
function save() {
clCache::save();
}
}