diff options
| author | 2023-01-18 10:12:21 +0100 | |
|---|---|---|
| committer | 2023-01-18 10:12:21 +0100 | |
| commit | daaa391e33c5d92e3dd91bb0b81ac420abed7097 (patch) | |
| tree | a3263c26ac90fb3115627e156eba580826acfd4f /lib/lib_opml.php | |
| parent | 216e39c3cc43061686981b96328796765d264d29 (diff) | |
tec: Update the lib_opml (#4403)
* fix: Fix undefined GLOB_BRACE on Alpine
The manual states that:
> Note: The GLOB_BRACE flag is not available on some non GNU systems,
> like Solaris or Alpine Linux.
This generated an error on Alpine.
Reference: https://www.php.net/manual/function.glob.php
* fix: List details of feeds for OPML exportation
The details are necessary to export the XPath information, the CSS full
content path and read actions filters.
* Update LibOpml to 0.4.0
* Refactor OPML importation to be more robust
First, it fixes two regressions introduced by the update of lib_opml:
- title attribute is used when text attribute is missing;
- the OPML category attribute is used as a fallback for feeds categories.
In a related way, if also fixes a problem when a feed had both a parent
category outline and a category attribute. Before, it only considered the
attribute as its category, but now it considers the parent outline.
Then, it counts category limit correctly by not increasing
`$nb_categories` if the category already exists.
* Exclude lib_opml from the CodeSniffer
* Fix variable names when logging some errors
* Fix catch of LibOpml Exception
* Make sure to declare the category
* Exclude lib_opml from PHPStan analyze
* Disable markdownlint for lib_opml
* Fix typos
* Use auto-loading and allow updates via Composer
* Fix broken links to lib_opml
* Bring back the ability to import the OPML frss:opmlUrl attribute
* Refactor the logs of OPML errors
* Update lib_opml to the version 0.5.0
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Diffstat (limited to 'lib/lib_opml.php')
| -rw-r--r-- | lib/lib_opml.php | 353 |
1 files changed, 0 insertions, 353 deletions
diff --git a/lib/lib_opml.php b/lib/lib_opml.php deleted file mode 100644 index f86d780b7..000000000 --- a/lib/lib_opml.php +++ /dev/null @@ -1,353 +0,0 @@ -<?php - -/** - * lib_opml is a free library to manage OPML format in PHP. - * - * By default, it takes in consideration version 2.0 but can be compatible with - * OPML 1.0. More information on http://dev.opml.org. - * Difference is "text" attribute is optional in version 1.0. It is highly - * recommended to use this attribute. - * - * lib_opml requires SimpleXML (php.net/simplexml) and DOMDocument (php.net/domdocument) - * - * @author Marien Fressinaud <dev@marienfressinaud.fr> - * @link https://github.com/marienfressinaud/lib_opml - * @version 0.2-FreshRSS~1.20.0 - * @license public domain - * - * Usages: - * > include('lib_opml.php'); - * > $filename = 'my_opml_file.xml'; - * > $opml_array = libopml_parse_file($filename); - * > print_r($opml_array); - * - * > $opml_string = [...]; - * > $opml_array = libopml_parse_string($opml_string); - * > print_r($opml_array); - * - * > $opml_array = [...]; - * > $opml_string = libopml_render($opml_array); - * > $opml_object = libopml_render($opml_array, true); - * > echo $opml_string; - * > print_r($opml_object); - * - * You can set $strict argument to false if you want to bypass "text" attribute - * requirement. - * - * If parsing fails for any reason (e.g. not an XML string, does not match with - * the specifications), a LibOPML_Exception is raised. - * - * lib_opml array format is described here: - * $array = array( - * 'head' => array( // 'head' element is optional (but recommended) - * 'key' => 'value', // key must be a part of available OPML head elements - * ), - * 'body' => array( // body is required - * array( // this array represents an outline (at least one) - * 'text' => 'value', // 'text' element is required if $strict is true - * 'key' => 'value', // key and value are what you want (optional) - * '@outlines' = array( // @outlines is a special value and represents sub-outlines - * array( - * [...] // where [...] is a valid outline definition - * ), - * ), - * ), - * array( // other outline definitions - * [...] - * ), - * [...], - * ) - * ) - * - */ - -/** - * A simple Exception class which represents any kind of OPML problem. - * Message should precise the current problem. - */ -class LibOPML_Exception extends Exception {} - - -// Define the list of available head attributes. All of them are optional. -define('HEAD_ELEMENTS', serialize(array( - 'title', 'dateCreated', 'dateModified', 'ownerName', 'ownerEmail', - 'ownerId', 'docs', 'expansionState', 'vertScrollState', 'windowTop', - 'windowLeft', 'windowBottom', 'windowRight' -))); - - -/** - * Parse an XML object as an outline object and return corresponding array - * - * @param SimpleXMLElement $outline_xml the XML object we want to parse - * @param bool $strict true if "text" attribute is required, false else - * @return array corresponding to an outline and following format described above - * @throws LibOPML_Exception - * @access private - */ -function libopml_parse_outline($outline_xml, $strict = true) { - $outline = array(); - - // An outline may contain any kind of attributes but "text" attribute is - // required ! - $text_is_present = false; - - $elem = dom_import_simplexml($outline_xml); - /** @var DOMAttr $attr */ - foreach ($elem->attributes as $attr) { - $key = $attr->localName; - - if ($attr->namespaceURI == '') { - $outline[$key] = $attr->value; - } else { - $outline[$key] = [ - 'namespace' => $attr->namespaceURI, - 'value' => $attr->value, - ]; - } - - if ($key === 'text') { - $text_is_present = true; - } - } - - if (!$text_is_present && $strict) { - throw new LibOPML_Exception( - 'Outline does not contain any text attribute' - ); - } - - if (empty($outline['text']) && isset($outline['title'])) { - $outline['text'] = $outline['title']; - } - - foreach ($outline_xml->children() as $key => $value) { - // An outline may contain any number of outline children - if ($key === 'outline') { - $outline['@outlines'][] = libopml_parse_outline($value, $strict); - } - } - - return $outline; -} - -/** - * Reformat the XML document as a hierarchy when - * the OPML 2.0 category attribute is used - */ -function preprocessing_categories($doc) { - $outline_categories = array(); - $body = $doc->getElementsByTagName('body')->item(0); - $xpath = new DOMXpath($doc); - $outlines = $xpath->query('/opml/body/outline[@category]'); - foreach ($outlines as $outline) { - $category = trim($outline->getAttribute('category')); - if ($category != '') { - $outline_category = null; - if (!isset($outline_categories[$category])) { - $outline_category = $doc->createElement('outline'); - $outline_category->setAttribute('text', $category); - $body->insertBefore($outline_category, $body->firstChild); - $outline_categories[$category] = $outline_category; - } else { - $outline_category = $outline_categories[$category]; - } - $outline->parentNode->removeChild($outline); - $outline_category->appendChild($outline); - } - } -} - -/** - * Parse a string as a XML one and returns the corresponding array - * - * @param string $xml is the string we want to parse - * @param bool $strict true to perform some validation (e.g. require "text" attribute), false to relax - * @return array corresponding to the XML string and following format described above - * @throws LibOPML_Exception - * @access public - */ -function libopml_parse_string($xml, $strict = true) { - $dom = new DOMDocument(); - $dom->recover = true; - $dom->strictErrorChecking = false; - $dom->loadXML($xml); - $dom->encoding = 'UTF-8'; - - //Partial compatibility with the category attribute of OPML 2.0 - preprocessing_categories($dom); - - $opml = simplexml_import_dom($dom); - - if (!$opml) { - throw new LibOPML_Exception(); - } - - $array = array( - 'version' => (string)$opml['version'], - 'head' => array(), - 'body' => array() - ); - - if (isset($opml->head)) { - // We get all "head" elements. Head is required but its sub-elements are optional. - foreach ($opml->head->children() as $key => $value) { - if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { - $array['head'][$key] = (string)$value; - } elseif ($strict) { - throw new LibOPML_Exception($key . ' is not part of the OPML 2.0 specification'); - } - } - } elseif ($strict) { - throw new LibOPML_Exception('Required OPML head element is missing!'); - } - - // Then, we get body oulines. Body must contain at least one outline - // element. - $at_least_one_outline = false; - foreach ($opml->body->children() as $key => $value) { - if ($key === 'outline') { - $at_least_one_outline = true; - $array['body'][] = libopml_parse_outline($value, $strict); - } - } - - if (!$at_least_one_outline) { - throw new LibOPML_Exception( - 'OPML body must contain at least one outline element' - ); - } - - return $array; -} - - -/** - * Parse a string contained into a file as a XML string and returns the corresponding array - * - * @param string $filename should indicates a valid XML file - * @param bool $strict true if "text" attribute is required, false else - * @return array corresponding to the file content and following format described above - * @throws LibOPML_Exception - * @access public - */ -function libopml_parse_file($filename, $strict = true) { - $file_content = file_get_contents($filename); - - if ($file_content === false) { - throw new LibOPML_Exception( - $filename . ' cannot be found' - ); - } - - return libopml_parse_string($file_content, $strict); -} - - -/** - * Create a XML outline object in a parent object. - * - * @param SimpleXMLElement $parent_elt is the parent object of current outline - * @param array $outline array representing an outline object - * @param bool $strict true if "text" attribute is required, false else - * @throws LibOPML_Exception - * @access private - */ -function libopml_render_outline($parent_elt, $outline, $strict) { - // Outline MUST be an array! - if (!is_array($outline)) { - throw new LibOPML_Exception( - 'Outline element must be defined as array' - ); - } - - $outline_elt = $parent_elt->addChild('outline'); - $text_is_present = false; - /** @var string|array<string,mixed> $value */ - foreach ($outline as $key => $value) { - // Only outlines can be an array and so we consider children are also - // outline elements. - if ($key === '@outlines' && is_array($value)) { - foreach ($value as $outline_child) { - libopml_render_outline($outline_elt, $outline_child, $strict); - } - } elseif (is_array($value) && !isset($value['namespace'])) { - throw new LibOPML_Exception( - 'Type of outline elements cannot be array (except for providing a namespace): ' . $key - ); - } else { - // Detect text attribute is present, that's good :) - if ($key === 'text') { - $text_is_present = true; - } - if (is_array($value)) { - if (!empty($value['namespace']) && !empty($value['value'])) { - $outline_elt->addAttribute($key, $value['value'], $value['namespace']); - } - } else { - $outline_elt->addAttribute($key, $value); - } - } - } - - if (!$text_is_present && $strict) { - throw new LibOPML_Exception( - 'You must define at least a text element for all outlines' - ); - } -} - - -/** - * Render an array as an OPML string or a XML object. - * - * @param array $array is the array we want to render and must follow structure defined above - * @param bool $as_xml_object false if function must return a string, true for a XML object - * @param bool $strict true if "text" attribute is required, false else - * @return string|SimpleXMLElement XML string corresponding to $array or XML object - * @throws LibOPML_Exception - * @access public - */ -function libopml_render($array, $as_xml_object = false, $strict = true) { - $opml = new SimpleXMLElement('<opml></opml>'); - $opml->addAttribute('version', $strict ? '2.0' : '1.0'); - - // Create head element. $array['head'] is optional but head element will - // exist in the final XML object. - $head = $opml->addChild('head'); - if (isset($array['head'])) { - foreach ($array['head'] as $key => $value) { - if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { - $head->addChild($key, $value); - } - } - } - - // Check body is set and contains at least one element - if (!isset($array['body'])) { - throw new LibOPML_Exception( - '$array must contain a body element' - ); - } - if (count($array['body']) <= 0) { - throw new LibOPML_Exception( - 'Body element must contain at least one element (array)' - ); - } - - // Create outline elements - $body = $opml->addChild('body'); - foreach ($array['body'] as $outline) { - libopml_render_outline($body, $outline, $strict); - } - - // And return the final result - if ($as_xml_object) { - return $opml; - } else { - $dom = dom_import_simplexml($opml)->ownerDocument; - $dom->formatOutput = true; - $dom->encoding = 'UTF-8'; - return $dom->saveXML(); - } -} |
