yf_rss_data class

RSS data handler.

Fileplugins/common/classes/common/yf_rss_data.class.php
Use_class('rss_data', 'classes/common/')

Public properties

A site sets them in its config with $PROJECT_CONF['rss_data']['NAME'] = ...;

PropertyDefaultDescription
$AGGR_CACHE_PATH
string
'uploads/rss_aggregator_cache/'
$DESC_TRUNC_SIZE
@int
500 Set to 0 to disable truncation
$FEEDS_CACHE_PATH
string
'uploads/rss_cache/'
$SHOW_RSS_ENCODING
@string
'utf-8' Leave empty for default
$USE_ARRAY_CACHE
bool
true Use serialized cache
$_decode_func null
$_latest_cached_items null
$_rss_feeds null
$avail_formats
array
['RSS0.91', 'RSS1.0', 'RSS2.0', 'MBOX', 'OPML', 'ATOM', ... (9 items)]
array (
  0 => 'RSS0.91',
  1 => 'RSS1.0',
  2 => 'RSS2.0',
  3 => 'MBOX',
  4 => 'OPML',
  5 => 'ATOM',
  6 => 'ATOM0.3',
  7 => 'HTML',
  8 => 'JS',
)
@conf_skip Valid format strings
$avail_item_fields
array
['title', 'link', 'description', 'date', 'source', 'author', ... (8 items)]
array (
  0 => 'title',
  1 => 'link',
  2 => 'description',
  3 => 'date',
  4 => 'source',
  5 => 'author',
  6 => 'comments',
  7 => 'guid',
)
@conf_skip Known item field names

Public methods 7

fetch_data

public function fetch_data($params = [])

Get data from RSS feeds and return it as array.

    public function fetch_data($params = [])
    {
        $body = null;
        // Templates names
        $STPL_MAIN = ! empty($params['stpl_main']) ? $params['stpl_main'] : 'system/common/get_rss_page_main';
        $STPL_ITEM = ! empty($params['stpl_item']) ? $params['stpl_item'] : 'system/common/get_rss_page_item';
        $NUM_ITEMS = ! empty($params['num_items']) ? $params['num_items'] : 15;
        // Get array of available org types
        $this->_rss_feeds = main()->get_data('rss_feeds');
        // Get latest records from RSS items cache
        $Q = db()->query('SELECT * FROM ' . db('rss_items') . ' ORDER BY feed_id,add_date DESC LIMIT ' . (int) (count((array) $this->_rss_feeds) * $NUM_ITEMS));
        while ($A = db()->fetch_assoc($Q)) {
            $this->_latest_cached_items[$A['feed_id']][$A['cache_md5']] = $A;
        }
        // Process feeds
        $body = null;
        foreach ((array) $this->_rss_feeds as $feed_id => $feed_info) {
            $output = null;
            $max_cache_date = null;
            $USING_DB_CACHE = false;
            $DB_CACHE_EXPIRED = false;
            // Try to find cached items
            if ( ! empty($this->_latest_cached_items[$feed_id])) {
                foreach ((array) $this->_latest_cached_items[$feed_id] as $item_info) {
                    $output['items'][] = [
                        'url' => $item_info['link'],
                        'title' => $item_info['title'],
                        'text' => $item_info['text'],
                        'pub_date' => $item_info['pub_date'],
                    ];
                    if ($item_info['pub_date'] > $max_cache_date) {
                        $max_cache_date = $item_info['add_date'];
                    }
                }
                $USING_DB_CACHE = true;
            }
            // Check if database cache is expired
            $DB_CACHE_EXPIRED = (bool) ($max_cache_date < time() - $feed_info['ttl']);
            // Parse page info directly from XML
            if ( ! $USING_DB_CACHE || ($USING_DB_CACHE && $DB_CACHE_EXPIRED)) {
                $output = $this->_get_rss_feed_array($feed_info['url'], $feed_info['ttl']);
            }
            // Skip empty feeds
            if (empty($output['items'])) {
                continue;
            }
            $items = '';
            // Process items
            foreach ((array) $output['items'] as $item_info) {
                $cache_md5 = md5($item_info['url'] . $item_info['title']);
                // Do save cache record
                if ( ! isset($this->_latest_cached_items[$feed_id][$cache_md5])) {
                    db()->REPLACE('rss_items', [
                        'feed_id' => (int) $feed_id,
                        'add_date' => time(),
                        'link' => _es($item_info['url']),
                        'title' => _es($item_info['title']),
                        'text' => _es($item_info['text']),
                        'pub_date' => strtotime($item_info['pub_date']),
                        'author' => _es($item_info['author']),
                        'cache_md5' => _es($cache_md5),
                        'guid' => _es($item_info['guid']),
                    ]);
                }
                // Process template
                $replace2 = [
                    'item_url' => $item_info['url'],
                    'item_title' => $item_info['title'],
                    'item_text' => $item_info['text'],
                ];
                $items .= tpl()->parse($STPL_ITEM, $replace2);
            }
            // Process template
            $replace = [
                'channel_url' => $feed_info['url'],
                'channel_title' => $feed_info['title'],
                'channel_desc' => $feed_info['desc'],
                'items' => $items,
            ];
            $body .= tpl()->parse($STPL_MAIN, $replace);
        }
        return $body;
    }

get_data

public function get_data($url = '', $params = [])

Get and display data from given RSS feed.

    public function get_data($url = '', $params = [])
    {
        $i = null;
        if ( ! $url) {
            return false;
        }
        // Templates names
        $STPL_MAIN = ! empty($params['stpl_main']) ? $params['stpl_main'] : 'system/common/get_rss_page_main';
        $STPL_ITEM = ! empty($params['stpl_item']) ? $params['stpl_item'] : 'system/common/get_rss_page_item';
        $NUM_ITEMS = ! empty($params['num_items']) ? $params['num_items'] : 15;
        $TTL = ! empty($params['ttl']) ? $params['ttl'] : 600;
        $LIMIT = ! empty($params['limit']) ? $params['limit'] : 10;

        $output = $this->_get_rss_feed_array($url, $TTL);
        // Skip empty feeds
        if (empty($output['items'])) {
            return false;
        }
        $items = '';
        $i = null;
        // Process items
        foreach ((array) $output['items'] as $item_info) {
            if ($LIMIT && $i++ > $LIMIT) {
                break;
            }
            $replace2 = [
                'item_url' => $item_info['url'],
                'item_title' => $item_info['title'],
                'item_text' => $item_info['text'],
            ];
            $items .= tpl()->parse($STPL_ITEM, $replace2);
        }
        // Process template
        $replace = [
            'channel_url' => $url,
            'channel_title' => $params['title'],
            'channel_desc' => $params['desc'],
            'items' => $items,
        ];
        return tpl()->parse($STPL_MAIN, $replace);
    }

show_rss_page

public function show_rss_page($data = [], $params = [])

Show given array as RSS page.

    public function show_rss_page($data = [], $params = [])
    {
        require_php_lib('yf_feedcreator');
        $rss = new UniversalFeedCreator();
        if ( ! isset($params['use_cached']) || ! empty($params['use_cached'])) {
            $rss->useCached();
        }
        $rss->title = _prepare_html( ! empty($params['feed_title']) ? $params['feed_title'] : 'Site feed title');
        $rss->description = _prepare_html( ! empty($params['feed_desc']) ? $params['feed_desc'] : 'Site feed description');
        // optional feed params
        $rss->descriptionTruncSize = ! empty($params['feed_trunc_size']) ? (int) ($params['item_trunc_size']) : 500;
        $rss->descriptionHtmlSyndicated = isset($params['feed_insert_html']) ? (bool) $params['feed_insert_html'] : true;
        $rss->link = _prepare_html(process_url(($params['feed_url'] ?? null) ?: './?object=' . ($_GET['object'] ?? null)));
        $this->self_link = $rss->link;
        $rss->syndicationURL = _prepare_html(process_url(($params['feed_source'] ?? null) ?: './?object=' . ($_GET['object'] ?? null) . '&action=' . ($_GET['action'] ?? null) . ( ! empty($_GET['id']) ? '&id=' . ($_GET['id'] ?? null) : '')));
        if ( ! empty($this->SHOW_RSS_ENCODING)) {
            $rss->encoding = $this->SHOW_RSS_ENCODING;
        }
        foreach ((array) $data as $A) {
            $item = new FeedItem();
            if (isset($A['date'])) {
                $A['date'] = (int) ($A['date']);
            }
            if (isset($A['link'])) {
                $A['link'] = process_url($A['link']);
            }
            // Process known fields
            foreach ((array) $this->avail_item_fields as $field_name) {
                if (isset($A[$field_name])) {
                    $item->$field_name = $A[$field_name];
                }
            }
            if ( ! isset($item->guid)) {
                $item->guid = $A['link'] . '#' . md5($params['feed_source'] . '&' . $A['date'] . '&' . $A['title'] . '&' . $A['author']);
            }
            // optional params
            $item->descriptionTruncSize = ! empty($params['item_trunc_size']) ? (int) ($params['item_trunc_size']) : $this->DESC_TRUNC_SIZE;
            $item->descriptionHtmlSyndicated = isset($params['item_insert_html']) ? (bool) $params['item_insert_html'] : true;
            // optional (enclosure)
            // Sample: $A['enclosure'] = array('url'=>'http://lh3.ggpht.com/smoliarov/Rwygj8ucrbE/AAAAAAAABIA/UkNlwQ7eniw/_200708.jpg','length'=>'65036','type'=>'image/jpeg');
            if ( ! empty($A['enclosure']) && is_array($A['enclosure'])) {
                $E = $A['enclosure'];
                $item->enclosure = new EnclosureItem();
                $item->enclosure->url = _prepare_html($E['url']);
                $item->enclosure->length = (int) ($E['length']);
                $item->enclosure->type = _prepare_html($E['type']);
            }
            $rss->addItem($item);
        }
        // Set format of the resulting feed
        $feed_format = isset($params['feed_format']) && in_array($params['feed_format'], $this->avail_formats) ? $params['feed_format'] : 'RSS2.0';
        $feed_file_name = $params['feed_file_name'] ?? '';
        if ( ! strlen($feed_file_name)) {
            $feed_file_name = md5(($_SERVER['HTTP_HOST'] ?? null) . '_' . $rss->title . $rss->description . $this->self_link);
        }
        $feed_file_name = common()->_propose_url_from_name($feed_file_name);
        $feed_cache_path = $this->FEEDS_CACHE_PATH . $feed_file_name . '.xml';
        $feed_cache_dir = dirname($feed_cache_path);
        if ( ! file_exists($feed_cache_dir)) {
            _mkdir_m($feed_cache_dir);
        }
        $feed_redirect = $params['return_feed_text'] ? false : true;
        $body = $rss->saveFeed($feed_format, $feed_cache_path, $feed_redirect);
        if ( ! empty($params['return_feed_text'])) {
            if ($body) {
                return $body;
            } elseif (file_exists($feed_cache_path)) {
                return file_get_contents($feed_cache_path);
            }
        } else {
            main()->NO_GRAPHICS = true;
            echo $body;
        }
    }

Methods starting with _ hooks and helpers; not routable as page actions

_decode_text

public function _decode_text($text = '')

Do decode text from feed using some tricks.

    public function _decode_text($text = '')
    {
        $func_name = $this->_decode_func;
        return str_replace(''', '\'', html_entity_decode($func_name($text)));
    }
public function _feed_encoding($rssDoc)

Special handling for newfeed encoding and possible conflicts with page encoding and PHP version.

    public function _feed_encoding($rssDoc)
    {
        // test if page is utf-8
        if (strpos(_ISO, 'utf') !== false || strpos(_ISO, 'UTF') !== false) {
            $encoding = 'html_entity_decode';
            // non utf-8 page
        } else {
            $encoding = fn ($v) => mb_convert_encoding($v, 'ISO-8859-1', 'UTF-8');
        }
        return $encoding;
    }
public function _get_rss_feed_array($rss_url = '', $feed_ttl = 3600, $num_items = 15)

Do get data from given feed.

    public function _get_rss_feed_array($rss_url = '', $feed_ttl = 3600, $num_items = 15)
    {
        require_php_lib('yf_domit');

        // Prepare cache params
        if ($this->USE_ARRAY_CACHE) {
            $_url_hash = md5($rss_url);
            $_obj_cache_path = $this->AGGR_CACHE_PATH . 'array_' . $_url_hash;
        }
        // Check if we can use array cache instead of loading domit library
        if ( ! empty($_obj_cache_path) && @file_exists($_obj_cache_path) && @filemtime($_obj_cache_path) > (time() - $feed_ttl)) {
            return @unserialize(file_get_contents($_obj_cache_path));
        }
        $rssDoc = new xml_domit_rss_document($rss_url, $this->AGGR_CACHE_PATH);

        $success = (bool) $rssDoc;
        // Stop here if no document parsed
        if ( ! $success) {
            return false;
        }
        $output = [
            'channel' => null,
            'items' => null,
        ];
        $totalChannels = $rssDoc->getChannelCount();
        // special handling for feed encoding
        $this->_decode_func = $this->_feed_encoding($rssDoc);
        // Process only first channel
        $cur_channel = &$rssDoc->getChannel(0);
        if (empty($cur_channel)) {
            return false;
        }
        $cur_ch_image = $cur_channel->getImage();
        $_ch_image = [];
        if (is_object($cur_ch_image)) {
            $_ch_image = [
                'title' => $this->_decode_text($cur_ch_image->getTitle()),
                'link' => $this->_decode_text($cur_ch_image->getLink()),
                'url' => $this->_decode_text($cur_ch_image->getUrl()),
                'width' => (int) ($cur_ch_image->getWidth()),
            ];
        }
        $output['channel'] = [
            'url' => $cur_channel->getLink(),
            'title' => $this->_decode_text($cur_channel->getTitle()),
            'desc' => $this->_decode_text($cur_channel->getDescription()),
            'webmaster' => $this->_decode_text($cur_channel->getWebMaster()),
            'image' => $_ch_image,
        ];
        // Get number of items
        $actual_items = $cur_channel->getItemCount();
        $total_items = ($num_items > $actual_items) ? $actual_items : $num_items;
        // Process items
        for ($j = 0; $j < $total_items; $j++) {
            $currItem = $cur_channel->getItem($j);
            $cur_enc = $currItem->getEnclosure();
            $_enclosure = [];
            if (is_object($cur_enc)) {
                $_enclosure = [
                    'url' => $this->_decode_text($cur_enc->getUrl()),
                    'length' => (int) ($cur_enc->getLength()),
                    'type' => $this->_decode_text($cur_enc->getType()),
                ];
            }
            $output['items'][$j] = [
                'url' => $currItem->getLink(),
                'title' => $this->_decode_text($currItem->getTitle()),
                'text' => $this->_decode_text($currItem->getDescription()),
                'pub_date' => $currItem->getPubDate(),
                'author' => $this->_decode_text($currItem->getAuthor()),
                'comments' => $this->_decode_text($currItem->getComments()),
                'enclosure' => $_enclosure,
            ];
        }
        // Do cache array
        if ($this->USE_ARRAY_CACHE) {
            file_put_contents($_obj_cache_path, serialize($output));
        }
        return $output;
    }

_init

public function _init()

Framework constructor.

    public function _init()
    {
        $this->FEEDS_CACHE_PATH = STORAGE_PATH . $this->FEEDS_CACHE_PATH;
        ! file_exists($this->FEEDS_CACHE_PATH) && _mkdir_m($this->FEEDS_CACHE_PATH);

        $this->AGGR_CACHE_PATH = STORAGE_PATH . $this->AGGR_CACHE_PATH;
        ! file_exists($this->AGGR_CACHE_PATH) && _mkdir_m($this->AGGR_CACHE_PATH);
    }