Zend_Feed_Reader es un componente utilizado para
consumir feeds RSS y Atom de cualquier versión, incluyendo
RDF/RSS 1.0,
RSS 2.0, Atom 0.3 y Atom 1.0. La API para
recuperar los datos del feed es
deliberadamente simple ya que Zend_Feed_Reader es
capaz de buscar en cualquier feed de cualquier tipo la información
solicitada a través de la API. Si los elementos típicos que contienen esta
información no están presentes, se adaptará y recurrirá a una
variedad de elementos alternativos en su lugar. Esta capacidad de elegir entre
alternativas elimina la necesidad de que los usuarios creen su propia
capa de abstracción sobre el componente para hacerlo útil o de tener
un conocimiento profundo de los estándares subyacentes, las
alternativas actuales y las extensiones con espacio de nombres.
Internamente, Zend_Feed_Reader funciona casi
por completo realizando consultas XPath contra el Document Object Model
del XML del feed. El DOM no se expone mediante una API de propiedades
encadenadas como Zend_Feed, aunque los
objetos subyacentes DOMDocument, DOMElement y DOMXPath se exponen para su manipulación
externa. Este enfoque singular de análisis es consistente y
el componente ofrece un sistema de plugins para añadir a la API de nivel de Feed y Entry
escribiendo Extensiones de manera similar.
El rendimiento se ve favorecido de tres maneras. En primer lugar,
Zend_Feed_Reader admite el uso de caché mediante
Zend_Cache para mantener una copia del
XML original del feed. Esto le permite omitir peticiones de red para una URI
de feed si
la caché es válida. En segundo lugar, la API de nivel de Feed y Entry está respaldada
por una caché interna (no persistente) de modo que las llamadas repetidas a la API para el
mismo feed evitarán un uso adicional del DOM o de XPath. En tercer lugar, importar
feeds desde una URI puede aprovechar
las peticiones HTTP Condicionales GET,
que permiten a los servidores emitir una respuesta 304 vacía cuando el
feed solicitado no ha cambiado desde la última vez que lo solicitó.
En este último caso, una instancia de Zend_Cache
mantendrá el último feed recibido junto con los valores de las cabeceras ETag y
Last-Modified enviados en la respuesta HTTP.
En relación con Zend_Feed,
Zend_Feed_Reader se concibió como un
reemplazo independiente de Zend_Feed, pero no
es compatible hacia atrás con Zend_Feed.
Más bien, es una alternativa que sigue una ideología diferente centrada
en ser sencillo de usar, flexible, consistente y ampliable mediante
el sistema de plugins. Zend_Feed_Reader tampoco
es capaz de construir feeds y delega esa responsabilidad
en Zend_Feed_Writer, su compañero de armas.
Importar un feed con Zend_Feed_Reader no
es muy diferente de hacerlo con Zend_Feed. Los feeds pueden
importarse desde una cadena, un archivo, una URI o una instancia de tipo
Zend_Feed_Abstract. Al importar desde una URI puede
utilizarse adicionalmente una petición HTTP Condicional GET.
Si la importación falla, se lanzará una excepción. El resultado final será un
objeto de tipo Zend_Feed_Reader_FeedInterface, cuyas
implementaciones principales son
Zend_Feed_Reader_Feed_Rss y
Zend_Feed_Reader_Feed_Atom
(¡Zend_Feed se quedó con todos los nombres cortos!). Ambos
objetos admiten múltiples versiones (todas las existentes) de estos tipos generales de feed.
En el siguiente ejemplo, importamos un feed RDF/RSS 1.0 y extraemos alguna información básica que puede guardarse en una base de datos o en otro lugar.
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
$data = array(
'title' => $feed->getTitle(),
'link' => $feed->getLink(),
'dateModified' => $feed->getDateModified(),
'description' => $feed->getDescription(),
'language' => $feed->getLanguage(),
'entries' => array(),
);
foreach ($feed as $entry) {
$edata = array(
'title' => $entry->getTitle(),
'description' => $entry->getDescription(),
'dateModified' => $entry->getDateModified(),
'authors' => $entry->getAuthors(),
'link' => $entry->getLink(),
'content' => $entry->getContent()
);
$data['entries'][] = $edata;
}
El ejemplo anterior demuestra
la API de Zend_Feed_Reader, y también
demuestra parte de su funcionamiento interno. En realidad, el feed
RDF seleccionado no tiene ningún elemento nativo de fecha o autor,
sin embargo utiliza el módulo Dublin Core 1.1, que ofrece
elementos de creador y fecha con espacio de nombres.
Zend_Feed_Reader recurre a estos y
opciones similares si no existen elementos nativos relevantes. Si
no puede encontrar en absoluto una alternativa, devolverá NULL,
indicando que no se pudo encontrar la información en el feed. Debe tener en cuenta
que las clases que implementan
Zend_Feed_Reader_FeedInterface también implementan
las interfaces SPL Iterator y
Countable.
Los feeds también pueden importarse desde cadenas, archivos, e incluso objetos de
tipo Zend_Feed_Abstract.
// desde una URI
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
// desde una cadena
$feed = Zend_Feed_Reader::importString($feedXmlString);
// desde un archivo
$feed = Zend_Feed_Reader::importFile('./feed.xml');
// desde un objeto Zend_Feed_Abstract
$zfeed = Zend_Feed::import('http://www.planet-php.net/atom/');
$feed = Zend_Feed_Reader::importFeed($zfeed);
Zend_Feed_Reader hace todo lo posible por no
encasillarle en un espacio limitado. Si necesita trabajar con un feed fuera de
Zend_Feed_Reader, puede extraer los objetos DOMDocument o DOMElement
base de cualquier clase, o incluso una cadena XML
que los contenga. También se proporcionan métodos para extraer el objeto DOMXPath actual
(con todos los espacios de nombres del núcleo y de las Extensiones registrados) y el prefijo correcto usado
en todas las consultas XPath para el Feed o Entry actual. Los métodos básicos
a utilizar (en cualquier objeto) son saveXml(),
getDomDocument(),
getElement(),
getXpath() y
getXpathPrefix(). Estos le permitirán liberarse
de Zend_Feed_Reader y hacer cualquier otra cosa
que desee.
saveXml()devuelve una cadena XML que contiene únicamente el elemento que representa el objeto actual.getDomDocument()devuelve el objeto DOMDocument que representa el feed completo (incluso si se llama desde un objeto Entry).getElement()devuelve el DOMElement del objeto actual (es decir, el Feed o el Entry actual).getXpath()devuelve el objeto DOMXPath del feed actual (incluso si se llama desde un objeto Entry) con los espacios de nombres del tipo de feed actual y todas las Extensiones cargadas preregistrados.getXpathPrefix()devuelve el prefijo de consulta para el objeto actual (es decir, el Feed o el Entry actual) que incluye la ruta de consulta XPath correcta para ese Feed o Entry específico.
He aquí un ejemplo donde un feed podría incluir una Extensión RSS no
soportada por Zend_Feed_Reader de forma predeterminada.
Cabe destacar que podría escribir y registrar una Extensión (tratado más adelante)
para hacer esto, pero eso no siempre se justifica para una comprobación rápida. Debe registrar cualquier
espacio de nombres nuevo en el objeto DOMXPath antes de usarlo, a menos que ya esté
registrado por Zend_Feed_Reader o por una
Extensión de antemano.
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
$xpathPrefix = $feed->getXpathPrefix();
$xpath = $feed->getXpath();
$xpath->registerNamespace('admin', 'http://webns.net/mvcb/');
$reportErrorsTo = $xpath->evaluate('string('
. $xpathPrefix
. '/admin:errorReportsTo)');
![]() |
Advertencia |
|---|---|
Si registra un espacio de nombres ya registrado con un nombre de prefijo diferente
al usado internamente por
|
Zend_Feed_Reader admite el uso de una
instancia de Zend_Cache para almacenar en caché feeds (como
XML) y así evitar peticiones de red innecesarias. Añadir una caché es aquí tan
sencillo como en otros componentes de Zend Framework: cree
y configure su caché y luego indique a
Zend_Feed_Reader que la use. La clave de caché
utilizada es "Zend_Feed_Reader_" seguida del hash
MD5 de la URI del feed.
$frontendOptions = array(
'lifetime' => 7200,
'automatic_serialization' => true
);
$backendOptions = array('cache_dir' => './tmp/');
$cache = Zend_Cache::factory(
'Core', 'File', $frontendOptions, $backendOptions
);
Zend_Feed_Reader::setCache($cache);
![]() |
Nota |
|---|---|
Aunque se aparte un poco del tema, también debería considerar
añadir una caché a
|
La gran pregunta que a menudo se hace al importar un feed con frecuencia es
si realmente ha cambiado. Con una caché habilitada, puede añadir soporte de
HTTP Condicional GET a su arsenal para responder esa
pregunta.
Usando este método, puede solicitar feeds desde URIs e incluir
los últimos valores conocidos de las cabeceras de respuesta ETag y Last-Modified
con la petición (usando las cabeceras If-None-Match e If-Modified-Since).
Si el feed en el servidor permanece sin cambios, debería
recibir una respuesta 304 que indica a
Zend_Feed_Reader que use la
versión en caché. Si se envía un feed completo en una respuesta con un código de estado
200, esto significa que el feed ha cambiado y
Zend_Feed_Reader analizará la nueva
versión y la guardará en la caché. También almacenará en caché los nuevos
valores de las cabeceras ETag y Last-Modified para uso futuro.
Estas peticiones "condicionales" no tienen garantizado el soporte
por parte del servidor al que se solicita una URI, pero pueden intentarse
de todos modos. Sin embargo, la mayoría de las fuentes de feed comunes, como los blogs, deberían
soportarlo. Para habilitar las peticiones condicionales, necesitará
proporcionar una caché a Zend_Feed_Reader.
$frontendOptions = array(
'lifetime' => 86400,
'automatic_serialization' => true
);
$backendOptions = array('cache_dir' => './tmp/');
$cache = Zend_Cache::factory(
'Core', 'File', $frontendOptions, $backendOptions
);
Zend_Feed_Reader::setCache($cache);
Zend_Feed_Reader::useHttpConditionalGet();
$feed = Zend_Feed_Reader::import('http://www.planet-php.net/rdf/');
En el ejemplo anterior, con las peticiones HTTP Condicionales
GET habilitadas, los valores de las cabeceras de respuesta ETag y
Last-Modified se almacenarán en caché junto con el feed. Durante las próximas 24 horas (el tiempo de vida
de la caché), los feeds solo se actualizarán en la caché si se recibe una respuesta distinta de 304
que contenga un documento XML RSS o Atom válido.
Si pretende gestionar las cabeceras de la petición desde fuera de
Zend_Feed_Reader, puede establecer las
cabeceras de petición If-None-Matches e If-Modified-Since correspondientes
mediante el método de importación por URI.
$lastEtagReceived = '5e6cefe7df5a7e95c8b1ba1a2ccaff3d';
$lastModifiedDateReceived = 'Wed, 08 Jul 2009 13:37:22 GMT';
$feed = Zend_Feed_Reader::import(
$uri, $lastEtagReceived, $lastModifiedDateReceived
);
Hoy en día, muchos sitios web son conscientes de que la ubicación de sus feeds
XML no siempre es obvia. Un pequeño gráfico
RDF, RSS o Atom ayuda cuando el usuario está leyendo la página, pero ¿qué ocurre cuando una máquina
visita la página intentando identificar dónde están ubicados sus feeds? Para ayudar en
esto, los sitios web pueden apuntar a sus feeds usando etiquetas <link> en
la sección <head> de su HTML. Para aprovechar esto,
puede usar Zend_Feed_Reader para localizar estos
feeds usando el método estático findFeedLinks().
Este método llama a cualquier URI y busca la ubicación de feeds RSS, RDF y Atom asumiendo que el HTML del sitio web contiene los enlaces relevantes. Luego devuelve un objeto de valor donde puede comprobar la existencia de una URI de feed RSS, RDF o Atom.
El objeto devuelto es una subclase de ArrayObject
llamada Zend_Feed_Reader_Collection_FeedLink, de modo que puede convertirla
a un array, o iterar sobre ella, para acceder a todos los enlaces detectados.
Sin embargo, como un atajo sencillo, puede simplemente tomar el primer enlace
RSS, RDF o Atom usando sus propiedades públicas como en el ejemplo siguiente.
De lo contrario, cada elemento del ArrayObject es un array simple
con las claves "type" y "uri" donde el type es uno de "rdf", "rss" o
"atom".
$links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');
if(isset($links->rdf)) {
echo $links->rdf, "\n"; // http://www.planet-php.org/rdf/
}
if(isset($links->rss)) {
echo $links->rss, "\n"; // http://www.planet-php.org/rss/
}
if(isset($links->atom)) {
echo $links->atom, "\n"; // http://www.planet-php.org/atom/
}
Basándose en estos enlaces, puede entonces importar desde la fuente que desee de la manera habitual.
Este método rápido solo le proporciona un enlace por cada tipo de feed, pero los sitios web pueden indicar muchos enlaces de cualquier tipo. Quizás se trate de un sitio de noticias con un feed RSS para cada categoría de noticias. Puede iterar sobre todos los enlaces usando el iterador del ArrayObject.
$links = Zend_Feed_Reader::findFeedLinks('http://www.planet-php.net');
foreach ($links as $link) {
echo $link['uri'], "\n";
}
En un intento por simplificar los tipos de retorno, con Zend Framework 1.10 los tipos
devueltos por los distintos métodos de nivel de feed y entrada pueden incluir un objeto
de tipo Zend_Feed_Reader_Collection_CollectionAbstract.
A pesar del nombre especial de la clase, que explicaré a continuación, esto es solo una simple
subclase de ArrayObject de SPL.
El propósito principal aquí es permitir la presentación de la mayor cantidad posible de datos de los elementos solicitados, a la vez que se permite el acceso a los datos más relevantes como un simple array. Esto también impone un enfoque estándar para devolver dichos datos, que anteriormente podían oscilar entre arrays y objetos.
El nuevo tipo de clase actúa de forma idéntica a ArrayObject
con la única adición de un nuevo método getValues()
que devuelve un simple array plano que contiene la información más relevante.
Un ejemplo sencillo de esto es
Zend_Feed_Reader_FeedInterface::getCategories(). Cuando se usa con
cualquier feed RSS o Atom, este método devolverá los datos de categoría como un
objeto contenedor llamado Zend_Feed_Reader_Collection_Category. El
objeto contenedor contendrá, por categoría, tres campos de datos: term, scheme y
label. El "term" es el nombre básico de la categoría, a menudo legible por máquina (es decir, funciona bien
con URIs). El scheme representa un esquema de categorización (normalmente un
identificador URI) también conocido como "domain" en RSS
2.0. El "label" es un nombre de categoría legible por humanos que admite
entidades HTML. En RSS 2.0 no existe un atributo label,
por lo que siempre se establece con el mismo valor que el term, por comodidad.
Para acceder a las etiquetas de categoría por sí mismas en un simple array de valores, podría hacer algo como:
$feed = Zend_Feed_Reader::import('http://www.example.com/atom.xml');
$categories = $feed->getCategories();
$labels = array();
foreach ($categories as $cat) {
$labels[] = $cat['label']
}
Es un ejemplo un tanto forzado, pero el punto es que las etiquetas están entrelazadas con otra información.
Sin embargo, la clase contenedora le permite acceder a los datos "más relevantes"
como un simple array usando el método getValues(). El concepto
de "más relevante" es obviamente una decisión subjetiva. Para las categorías significa las etiquetas
de categoría (no los terms ni los schemes), mientras que para los autores serían sus nombres
(no sus direcciones de correo electrónico ni URIs). El array simple es plano (solo
valores) y se pasa por array_unique() para eliminar
duplicados.
$feed = Zend_Feed_Reader::import('http://www.example.com/atom.xml');
$categories = $feed->getCategories();
$labels = $categories->getValues();
El ejemplo anterior muestra cómo extraer únicamente las etiquetas y nada más, ofreciendo así un acceso sencillo a las etiquetas de categoría sin trabajo adicional para extraer esos datos por sí mismos.
Recuperar información de un feed (cubriremos las entradas e items en la
siguiente sección, aunque siguen los mismos principios) utiliza una API claramente
definida que es exactamente la misma independientemente de si el feed en
cuestión es RSS, RDF o Atom. Lo mismo aplica para las
subversiones de estos estándares, y hemos probado cada una de las versiones de
RSS y Atom. Aunque
el XML subyacente del feed puede diferir sustancialmente en términos de las
etiquetas y elementos que presentan, no obstante todos intentan
transmitir información similar y para reflejar esto todas las diferencias
y disparidades entre etiquetas alternativas son gestionadas internamente por
Zend_Feed_Reader, presentándole una
interfaz idéntica para cada uno. Idealmente, no debería tener que preocuparse por
si un feed es RSS o Atom, siempre que pueda extraer la
información que desea.
![]() |
Nota |
|---|---|
Aunque determinar el terreno común entre los tipos de feed es en sí mismo complejo, cabe señalar que RSS en particular es una "especificación" constantemente disputada. Esto tiene sus raíces en el documento original de RSS 2.0, que contiene ambigüedades y no detalla el tratamiento correcto de todos los elementos. Como resultado, este componente aplica rigurosamente la Especificación RSS 2.0.11 publicada por la RSS Advisory Board y su Perfil de Mejores Prácticas de RSS que la acompaña. No se soportará ninguna otra interpretación de RSS 2.0, aunque pueden permitirse excepciones cuando no impidan directamente la aplicación de los dos documentos mencionados anteriormente. |
Por supuesto, no vivimos en un mundo ideal, por lo que puede haber ocasiones en las que la
API simplemente no cubra lo que está buscando. Para ayudarle,
Zend_Feed_Reader ofrece un sistema de plugins que
le permite escribir Extensiones para ampliar la API del núcleo y cubrir cualquier
dato adicional que esté tratando de extraer de los feeds. Si escribir
otra Extensión resulta demasiado complicado, puede simplemente tomar los
objetos DOM o XPath subyacentes y hacerlo manualmente en su
aplicación. Por supuesto, realmente le animamos a escribir una Extensión
simplemente para hacerla más portable y reutilizable, y las Extensiones útiles pueden proponerse
al Framework para su incorporación formal.
He aquí un resumen de la API del núcleo para Feeds. Debe tener en cuenta que
comprende no solo los estándares básicos de RSS y Atom, sino que también
tiene en cuenta una serie de Extensiones incluidas junto con
Zend_Feed_Reader. La nomenclatura de estos
métodos provenientes de Extensiones sigue siendo bastante genérica: todos los métodos de las Extensiones
operan al mismo nivel que la API del núcleo, aunque le permitimos
recuperar cualquier objeto de Extensión específico por separado si es necesario.
Tabla 33.1. Métodos de la API a nivel de Feed
getId() |
Devuelve un ID único asociado con este feed |
getTitle() |
Devuelve el título del feed |
getDescription() |
Devuelve la descripción de texto del feed. |
getLink() |
Devuelve una URI al sitio web HTML que contiene la misma información o similar a este feed (es decir, si el feed proviene de un blog, debería proporcionar la URI del blog donde se puede leer la versión HTML de las entradas). |
getFeedLink() |
Devuelve la URI de este feed, que puede ser la misma URI usada para importar el feed. Hay casos importantes en los que el enlace del feed puede diferir porque la URI de origen se está actualizando y se pretende eliminarla en el futuro. |
getAuthors() |
Devuelve un objeto de tipo
Zend_Feed_Reader_Collection_Author, que es un
ArrayObject cuyos elementos son cada uno arrays simples
que contienen cualquier combinación de las claves "name", "email" y "uri". Cuando
no sean relevantes para los datos de origen, algunas de estas claves pueden omitirse.
|
getAuthor(integer $index = 0) |
Devuelve el primer autor conocido, o con el
parámetro opcional $index cualquier índice
específico del array de Authors descrito anteriormente (devolviendo
NULL si el índice no es válido).
|
getDateCreated() |
Devuelve la fecha en la que se creó este feed. Generalmente
solo aplica a Atom, donde representa la fecha en que se creó el recurso
descrito por un documento Atom 1.0. La fecha devuelta
será un objeto Zend_Date.
|
getDateModified() |
Devuelve la fecha en la que este feed se modificó por última vez. La fecha devuelta
será un objeto Zend_Date.
|
getLastBuildDate() |
Devuelve la fecha en la que este feed se construyó por última vez. La fecha devuelta
será un objeto Zend_Date. Esto solo es
compatible con RSS: los feeds Atom siempre devolverán
NULL.
|
getLanguage() |
Devuelve el idioma del feed (si está definido) o simplemente el idioma indicado en el documento XML. |
getGenerator() |
Devuelve el generador del feed, por ejemplo, el software que lo generó. Esto puede diferir entre RSS y Atom ya que Atom define una notación distinta. |
getCopyright() |
Devuelve cualquier aviso de copyright asociado con el feed. |
getHubs() |
Devuelve un array de todos los endpoints URI de servidores Hub anunciados por el feed para su uso con el Protocolo Pubsubhubbub, permitiendo suscripciones al feed para actualizaciones en tiempo real. |
getCategories() |
Devuelve un objeto Zend_Feed_Reader_Collection_Category
que contiene los detalles de las categorías asociadas con el
feed general. Los campos soportados incluyen "term" (el nombre de categoría legible
por máquina), "scheme" (el esquema de categorización y dominio para esta
categoría), y "label" (un nombre de categoría legible por humanos, decodificado de
HTML). Cuando cualquiera de los tres campos está ausente,
se establecen al valor alternativo más cercano disponible o, en el caso
de "scheme", se establece en NULL.
|
getImage() |
Devuelve un array que contiene datos relacionados con cualquier imagen o logo del feed,
o NULL si no se encuentra ninguna imagen. El array resultante puede
contener las siguientes claves: uri,
link, title,
description, height, y
width. Los logos de Atom solo contienen una
URI, por lo que el resto de los metadatos se toman
únicamente de feeds RSS.
|
Dada la variedad de feeds existentes, algunos de estos métodos
sin duda devolverán NULL, indicando que la información relevante
no pudo localizarse. Cuando sea posible, Zend_Feed_Reader
recurrirá a elementos alternativos durante su búsqueda. Por
ejemplo, buscar una fecha de modificación en un feed RSS es más
complicado de lo que parece. Los feeds RSS 2.0 deberían incluir una
etiqueta <lastBuildDate> y (o) un
elemento <pubDate>. Pero ¿qué ocurre si no lo hace, tal vez
se trate de un feed RSS 1.0? Quizás en su lugar tenga un
elemento <atom:updated> con información idéntica
(Atom puede usarse para complementar la sintaxis de RSS). De no ser así,
podríamos simplemente mirar las entradas, elegir la más reciente y usar su
elemento <pubDate>. Suponiendo que exista... Muchos
feeds también usan elementos <dc:date> de Dublin Core 1.0 o 1.1
para feeds y entradas. O podríamos encontrar a Atom acechando de nuevo.
El punto es que Zend_Feed_Reader fue diseñado
para saber esto. Cuando solicita la fecha de modificación (o cualquier
otra cosa), buscará todas estas alternativas hasta que
se rinda y devuelva NULL, o encuentre una
alternativa que debería tener la respuesta correcta.
Además de los métodos anteriores, todos los objetos Feed implementan métodos para recuperar los objetos DOM y XPath de los feeds actuales, según se describió anteriormente. Los objetos Feed también implementan las interfaces SPL Iterator y Countable. La API extendida se resume a continuación.
Tabla 33.2. Métodos de la API extendida a nivel de Feed
getDomDocument() |
Devuelve el objeto DOMDocument padre para todo el documento XML de origen |
getElement() |
Devuelve el objeto DOMElement de nivel de feed actual |
saveXml() |
Devuelve una cadena que contiene un documento XML del elemento del feed completo (esto no es el documento original sino una versión reconstruida) |
getXpath() |
Devuelve el objeto DOMXPath usado internamente para ejecutar consultas sobre el objeto DOMDocument (esto incluye espacios de nombres del núcleo y de las Extensiones preregistrados) |
getXpathPrefix() |
Devuelve el prefijo de ruta DOM válido antepuesto a todas las consultas XPath que coinciden con el feed consultado |
getEncoding() |
Devuelve la codificación del documento XML de origen (nota: esto no puede tener en cuenta errores como que el servidor envíe documentos en una codificación diferente). Cuando no está definida, se aplica la codificación UTF-8 predeterminada de Unicode. |
count() |
Devuelve un recuento de las entradas o items que contiene este feed
(implementa la interfaz SPL Countable)
|
current() |
Devuelve la entrada actual (usando el índice actual
de key())
|
key() |
Devuelve el índice de la entrada actual |
next() |
Incrementa el valor del índice de entrada en uno |
rewind() |
Reinicia el índice de entrada a 0 |
valid() |
Comprueba que el índice de la entrada actual sea válido, es decir, que no sea inferior a 0 y no exceda el número de entradas existentes. |
getExtensions() |
Devuelve un array de todos los objetos de Extensión cargados para el feed actual (nota: existen tanto Extensiones a nivel de feed como a nivel de entrada, y aquí solo se devuelven las Extensiones a nivel de feed). Las claves del array tienen la forma {ExtensionName}_Feed. |
getExtension(string $name) |
Devuelve un objeto de Extensión para el feed registrado bajo el nombre proporcionado. Esto permite un acceso más detallado a las Extensiones que de otro modo podrían estar ocultas dentro de la implementación de los métodos estándar de la API. |
getType() |
Devuelve una constante estática de la clase (por ejemplo,
Zend_Feed_Reader::TYPE_ATOM_03,
es decir, Atom 0.3) que indica exactamente qué tipo de feed
se está consumiendo.
|
Recuperar información de entradas o items específicos (dependiendo de
si habla de Atom o de RSS) es idéntico a los datos a nivel de feed.
Acceder a las entradas es simplemente cuestión de iterar sobre un objeto Feed
o usar la interfaz SPL Iterator que implementan los objetos Feed
y llamar al método apropiado en cada uno.
Tabla 33.3. Métodos de la API a nivel de Entry
getId() |
Devuelve un ID único para la entrada actual. |
getTitle() |
Devuelve el título de la entrada actual. |
getDescription() |
Devuelve una descripción de la entrada actual. |
getLink() |
Devuelve una URI a la versión HTML de la entrada actual. |
getPermaLink() |
Devuelve el enlace permanente a la entrada actual. En la mayoría de los casos,
es lo mismo que usar getLink().
|
getAuthors() |
Devuelve un objeto de tipo
Zend_Feed_Reader_Collection_Author, que es un
ArrayObject cuyos elementos son cada uno arrays simples
que contienen cualquier combinación de las claves "name", "email" y "uri". Cuando
no sean relevantes para los datos de origen, algunas de estas claves pueden omitirse.
|
getAuthor(integer $index = 0) |
Devuelve el primer autor conocido, o con el
parámetro opcional $index cualquier índice
específico del array de Authors descrito anteriormente (devolviendo
NULL si el índice no es válido).
|
getDateCreated() |
Devuelve la fecha en la que se creó la entrada actual. Generalmente solo aplica a Atom, donde representa la fecha en que se creó el recurso descrito por un documento Atom 1.0. |
getDateModified() |
Devuelve la fecha en que la entrada actual se modificó por última vez |
getContent() |
Devuelve el contenido de la entrada actual (con cualquier entidad revertida si es posible, suponiendo que el tipo de contenido sea HTML). Se devuelve la descripción si no existe un elemento de contenido separado. |
getEnclosure() |
Devuelve un array que contiene el valor de todos los atributos de un elemento <enclosure> multimedia, incluyendo como claves del array: url, length, type. De acuerdo con el Perfil de Mejores Prácticas de RSS de la RSS Advisory Board, no se ofrece soporte para múltiples enclosures, ya que dicho soporte no forma parte de la especificación de RSS. |
getCommentCount() |
Devuelve el número de comentarios realizados en esta entrada en el momento en que se generó el feed por última vez |
getCommentLink() |
Devuelve una URI que apunta a la página HTML donde se pueden hacer comentarios sobre esta entrada |
getCommentFeedLink([string $type =
'atom'|'rss'])
|
Devuelve una URI que apunta a un feed del tipo indicado que contiene todos los comentarios de esta entrada (el tipo por defecto es Atom/RSS según el tipo de feed actual). |
getCategories() |
Devuelve un objeto Zend_Feed_Reader_Collection_Category
que contiene los detalles de las categorías asociadas con la
entrada. Los campos soportados incluyen "term" (el nombre de categoría legible
por máquina), "scheme" (el esquema de categorización y dominio para esta
categoría), y "label" (un nombre de categoría legible por humanos, decodificado de
HTML). Cuando cualquiera de los tres campos está ausente,
se establecen al valor alternativo más cercano disponible o, en el caso
de "scheme", se establece en NULL.
|
La API extendida para entradas es idéntica a la de los feeds, con la excepción de los métodos del Iterator, que no son necesarios aquí.
![]() |
Precaución |
|---|---|
|
A menudo existe confusión sobre los conceptos de fechas de modificación y de
creación. En Atom, se trata de dos conceptos claramente definidos
(así que no se preocupe), pero en RSS son vagos.
RSS 2.0
define un único elemento <pubDate>
que típicamente se refiere a la fecha en que se publicó esta entrada,
es decir, una especie de fecha de creación. Esto no siempre es así, y
puede cambiar con actualizaciones o no. Como resultado, si realmente
desea comprobar si una entrada ha cambiado, no confíe en los
resultados de
Para complicar aún más las cosas,
las fechas en los feeds pueden seguir diferentes estándares. Las fechas de Atom y
de Dublin Core deberían seguir ISO 8601,
y las fechas de RSS deberían
seguir RFC 822 o RFC 2822,
lo cual también es común. Los métodos de fecha
lanzarán una excepción si |
![]() |
Advertencia |
|---|---|
Los valores devueltos por estos métodos no se validan. Esto
significa que los usuarios deben realizar validación sobre todos los datos recuperados,
incluido el filtrado de cualquier HTML, como el proveniente de
|
Tabla 33.4. Métodos de la API extendida a nivel de Entry
getDomDocument() |
Devuelve el objeto DOMDocument padre para todo el feed (no solo la entrada actual) |
getElement() |
Devuelve el objeto DOMElement de nivel de entrada actual |
getXpath() |
Devuelve el objeto DOMXPath usado internamente para ejecutar consultas sobre el objeto DOMDocument (esto incluye espacios de nombres del núcleo y de las Extensiones preregistrados) |
getXpathPrefix() |
Devuelve el prefijo de ruta DOM válido antepuesto a todas las consultas XPath que coinciden con la entrada consultada |
getEncoding() |
Devuelve la codificación del documento XML de origen (nota: esto no puede tener en cuenta errores como que el servidor envíe documentos en una codificación diferente). La codificación predeterminada aplicada en ausencia de cualquier otra es la codificación UTF-8 de Unicode. |
getExtensions() |
Devuelve un array de todos los objetos de Extensión cargados para la entrada actual (nota: existen tanto Extensiones a nivel de feed como a nivel de entrada, y aquí solo se devuelven las Extensiones a nivel de entrada). Las claves del array tienen la forma {ExtensionName}_Entry. |
getExtension(string $name) |
Devuelve un objeto de Extensión para la entrada registrada bajo el nombre proporcionado. Esto permite un acceso más detallado a las Extensiones que de otro modo podrían estar ocultas dentro de la implementación de los métodos estándar de la API. |
getType() |
Devuelve una constante estática de la clase (por ejemplo,
Zend_Feed_Reader::TYPE_ATOM_03,
es decir, Atom 0.3) que indica exactamente qué tipo
de feed se está consumiendo.
|
Extender Zend_Feed_Reader le permite añadir
métodos tanto a nivel de feed como de entrada que cubran la recuperación
de información no soportada aún por
Zend_Feed_Reader. Dado el número de
extensiones de RSS y
Atom que existen, esto es algo bueno ya que
Zend_Feed_Reader no podría posiblemente añadir
todo.
Existen dos tipos posibles de Extensiones: aquellas que recuperan
información de elementos que son hijos inmediatos del elemento
raíz (por ejemplo, <channel> para RSS o
<feed> para Atom) y aquellas que recuperan
información de elementos hijos de una entrada (por ejemplo,
<item> para RSS o
<entry> para Atom). En el sistema de archivos, estas se agrupan como
clases dentro de un espacio de nombres basado en el nombre del estándar de la extensión. Por ejemplo,
internamente tenemos las clases Zend_Feed_Reader_Extension_DublinCore_Feed
y Zend_Feed_Reader_Extension_DublinCore_Entry,
que son dos Extensiones que implementan el soporte de Dublin Core
1.0 y 1.1.
Las Extensiones se cargan en Zend_Feed_Reader
usando Zend_Loader_PluginLoader, por lo que su funcionamiento
le resultará familiar por otros componentes de Zend Framework.
Zend_Feed_Reader ya incluye varias
de estas Extensiones; sin embargo, aquellas que no se usan internamente ni se
registran de forma predeterminada (las llamadas Extensiones del núcleo) deben registrarse
en Zend_Feed_Reader antes de usarse. Las
Extensiones incluidas son:
Tabla 33.5. Extensiones del núcleo (preregistradas)
| DublinCore (Feed y Entry) | Implementa el soporte para Dublin Core Metadata Element Set 1.0 y 1.1 |
| Content (solo Entry) | Implementa el soporte para Content 1.0 |
| Atom (Feed y Entry) | Implementa el soporte para Atom 0.3 y Atom 1.0 |
| Slash | Implementa el soporte para el módulo Slash de RSS 1.0 |
| WellFormedWeb | Implementa el soporte para Well Formed Web CommentAPI 1.0 |
| Thread | Implementa el soporte para las Atom Threading Extensions descritas en RFC 4685 |
| Podcast |
Implementa el soporte para la DTD Podcast 1.0 de
Apple
|
Las Extensiones del núcleo son algo especiales ya que son extremadamente comunes y multifacéticas. Por ejemplo, tenemos una Extensión del núcleo para Atom. Atom se implementa como una Extensión (no solo como una clase base) porque también funciona como un módulo RSS válido: se pueden insertar elementos de Atom en feeds RSS. Incluso he visto feeds RDF que usan mucho Atom en lugar de Extensiones más comunes como Dublin Core.
Tabla 33.6. Extensiones no incluidas en el núcleo (deben registrarse manualmente)
| Syndication | Implementa el soporte de Syndication 1.0 para feeds RSS |
| CreativeCommons | Un módulo de RSS que añade un elemento a nivel de <channel> o <item> que especifica qué licencia de Creative Commons aplica. |
Las Extensiones adicionales que no son del núcleo se ofrecen pero no se registran en
Zend_Feed_Reader de forma predeterminada. Si desea
usarlas, deberá indicar a
Zend_Feed_Reader que las cargue antes de
importar un feed. Se incluirán Extensiones adicionales que no son del núcleo
en futuras iteraciones del componente.
Registrar una Extensión con
Zend_Feed_Reader, de modo que se cargue y su API
esté disponible para los objetos Feed y Entry, es algo sencillo usando el
Zend_Loader_PluginLoader. Aquí registramos
la Extensión opcional Slash, y descubrimos que puede llamarse directamente
desde la API de nivel de Entry sin ningún esfuerzo. Tenga en cuenta que
los nombres de las Extensiones distinguen entre mayúsculas y minúsculas y usan camel case para
términos múltiples.
Zend_Feed_Reader::registerExtension('Syndication');
$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
$updatePeriod = $feed->current()->getUpdatePeriod();
En el sencillo ejemplo anterior, comprobamos con qué frecuencia se actualiza un feed
usando el método getUpdatePeriod().
Dado que no forma parte de la API del núcleo de
Zend_Feed_Reader, solo podía tratarse de
un método soportado por la recién registrada Extensión Syndication.
Como también puede notar, los nuevos métodos de las Extensiones son accesibles desde la API principal usando los métodos mágicos de PHP. Como alternativa, también puede acceder directamente a cualquier objeto de Extensión con un resultado similar, como se ve a continuación.
Zend_Feed_Reader::registerExtension('Syndication');
$feed = Zend_Feed_Reader::import('http://rss.slashdot.org/Slashdot/slashdot');
$syndication = $feed->getExtension('Syndication');
$updatePeriod = $syndication->getUpdatePeriod();
Inevitablemente, habrá ocasiones en las que la
API de Zend_Feed_Reader no sea capaz
de obtener algo que necesita de un feed o una entrada. Puede usar
los objetos de origen subyacentes, como DOMDocument, para obtenerlos manualmente; sin embargo,
hay un método más reutilizable disponible escribiendo Extensiones
que soporten estas nuevas consultas.
Como ejemplo, tomemos el caso de una corporación puramente ficticia llamada Jungle Books. Jungle Books ha estado publicando muchas reseñas de los libros que vende (procedentes de fuentes externas y clientes), distribuidas como un feed RSS 2.0. Su departamento de marketing se da cuenta de que las aplicaciones web que usan este feed no pueden actualmente averiguar exactamente qué libro se está reseñando. Para facilitarle la vida a todos, determinan que el departamento técnico necesita extender RSS 2.0 para incluir un nuevo elemento por entrada que proporcione el número ISBN-10 o ISBN-13 de la publicación a la que se refiere la entrada. Definen el nuevo elemento <isbn> de manera bastante simple con un nombre estándar y una URI de espacio de nombres:
JungleBooks 1.0: http://example.com/junglebooks/rss/module/1.0/
Un fragmento de RSS que contiene esta extensión en la práctica podría ser algo similar a:
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:jungle="http://example.com/junglebooks/rss/module/1.0/">
<channel>
<title>Jungle Books Customer Reviews</title>
<link>http://example.com/junglebooks</link>
<description>Many book reviews!</description>
<pubDate>Fri, 26 Jun 2009 19:15:10 GMT</pubDate>
<jungle:dayPopular>
http://example.com/junglebooks/book/938
</jungle:dayPopular>
<item>
<title>Review Of Flatland: A Romance of Many Dimensions</title>
<link>http://example.com/junglebooks/review/987</link>
<author>Confused Physics Student</author>
<content:encoded>
A romantic square?!
</content:encoded>
<pubDate>Thu, 25 Jun 2009 20:03:28 -0700</pubDate>
<jungle:isbn>048627263X</jungle:isbn>
</item>
</channel>
</rss>
Implementar este nuevo elemento ISBN como una simple extensión a nivel de entrada requeriría la siguiente clase (usando su propio espacio de nombres de clase fuera de Zend).
class My_FeedReader_Extension_JungleBooks_Entry
extends Zend_Feed_Reader_Extension_EntryAbstract
{
public function getIsbn()
{
if (isset($this->_data['isbn'])) {
return $this->_data['isbn'];
}
$isbn = $this->_xpath->evaluate(
'string(' . $this->getXpathPrefix() . '/jungle:isbn)'
);
if (!$isbn) {
$isbn = null;
}
$this->_data['isbn'] = $isbn;
return $this->_data['isbn'];
}
protected function _registerNamespaces()
{
$this->_xpath->registerNamespace(
'jungle', 'http://example.com/junglebooks/rss/module/1.0/'
);
}
}
Esta extensión es bastante fácil de seguir. Crea un nuevo método
getIsbn() que ejecuta una consulta XPath sobre
la entrada actual para extraer el número ISBN incluido en el
elemento <jungle:isbn>. Opcionalmente puede
almacenarlo en la caché interna no persistente (no es necesario seguir
consultando el DOM si se vuelve a llamar en la misma entrada). El
valor se devuelve a quien lo llama. Al final tenemos un método
protegido (es abstracto, por lo que debe existir) que registra el
espacio de nombres de Jungle Books para su módulo RSS personalizado. Aunque lo
llamemos un módulo RSS, no hay nada que impida que el mismo
elemento se use en feeds Atom, y todas las Extensiones que usan
el prefijo proporcionado por getXpathPrefix()
son en realidad neutrales y funcionan tanto en feeds RSS como Atom sin
código adicional.
Dado que esta Extensión se almacena fuera de Zend Framework, deberá
registrar la ruta de prefijo para sus Extensiones de modo que
Zend_Loader_PluginLoader pueda encontrarlas.
Después de eso, simplemente hay que registrar la Extensión,
si aún no está cargada, y usarla en la práctica.
if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
Zend_Feed_Reader::addPrefixPath(
'My_FeedReader_Extension', '/path/to/My/FeedReader/Extension'
);
Zend_Feed_Reader::registerExtension('JungleBooks');
}
$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
// ISBN for whatever book the first entry in the feed was concerned with
$firstIsbn = $feed->current()->getIsbn();
Escribir una Extensión a nivel de feed no es muy diferente. El
feed de ejemplo anterior incluía un elemento
<jungle:dayPopular> sin mencionar, que Jungle
Books ha añadido a su estándar para incluir un enlace al
libro más popular del día (en términos de tráfico de visitantes). Aquí
hay una Extensión que añade un método
getDaysPopularBookLink() a la
API a nivel de feed.
class My_FeedReader_Extension_JungleBooks_Feed
extends Zend_Feed_Reader_Extension_FeedAbstract
{
public function getDaysPopularBookLink()
{
if (isset($this->_data['dayPopular'])) {
return $this->_data['dayPopular'];
}
$dayPopular = $this->_xpath->evaluate(
'string(' . $this->getXpathPrefix() . '/jungle:dayPopular)'
);
if (!$dayPopular) {
$dayPopular = null;
}
$this->_data['dayPopular'] = $dayPopular;
return $this->_data['dayPopular'];
}
protected function _registerNamespaces()
{
$this->_xpath->registerNamespace(
'jungle', 'http://example.com/junglebooks/rss/module/1.0/'
);
}
}
Repitamos el último ejemplo usando una Extensión personalizada para mostrar el método en uso.
if(!Zend_Feed_Reader::isRegistered('JungleBooks')) {
Zend_Feed_Reader::addPrefixPath(
'My_FeedReader_Extension', '/path/to/My/FeedReader/Extension'
);
Zend_Feed_Reader::registerExtension('JungleBooks');
}
$feed = Zend_Feed_Reader::import('http://example.com/junglebooks/rss');
// URI to the information page of the day's most popular book with visitors
$daysPopularBookLink = $feed->getDaysPopularBookLink();
// ISBN for whatever book the first entry in the feed was concerned with
$firstIsbn = $feed->current()->getIsbn();
Repasando estos ejemplos, notará que no registramos las Extensiones
de feed y de entrada por separado. Las Extensiones dentro del mismo
estándar pueden incluir o no tanto una clase de feed como de entrada, por lo que
Zend_Feed_Reader solo requiere que
registre el nombre general, por ejemplo, JungleBooks, DublinCore,
Slash. Internamente, puede comprobar en qué nivel existen las Extensiones
y cargarlas si se encuentran. En nuestro caso, tenemos un conjunto completo de
Extensiones: JungleBooks_Feed y
JungleBooks_Entry.
![[Warning]](images/warning.png)
![[Note]](images/note.png)
![[Caution]](images/caution.png)