first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,399 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Abstract parser for TYPO3's extension.xml file.
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
abstract class AbstractExtensionXmlParser extends AbstractXmlParser
{
/**
* Keeps current author company of an extension's version.
*
* @var string
*/
protected $authorcompany;
/**
* Keeps current author mail address of an extension's version.
*
* @var string
*/
protected $authoremail;
/**
* Keeps current author name of an extension's version.
*
* @var string
*/
protected $authorname;
/**
* Keeps current category of an extension's version.
*
* @var string
*/
protected $category;
/**
* Keeps current dependencies of an extension's version.
*
* @var string
*/
protected $dependencies;
/**
* Keeps current description of an extension's version.
*
* @var string
*/
protected $description;
/**
* Keeps current download number sum of all extension's versions.
*
* @var string
*/
protected $extensionDownloadCounter;
/**
* Keeps current key of an extension.
*
* @var string
*/
protected $extensionKey;
/**
* Keeps current upload date of an extension's version.
*
* @var string
*/
protected $lastuploaddate;
/**
* Keeps current owner username of an extension's version.
*
* @var string
*/
protected $ownerusername;
/**
* Keeps current reviewstate of an extension's version.
*
* @var string
*/
protected $reviewstate;
/**
* Keeps current state of an extension's version.
*
* @var string
*/
protected $state;
/**
* Keeps current t3x file hash of an extension's version.
*
* @var string
*/
protected $t3xfilemd5;
/**
* Keeps current title of an extension's version.
*
* @var string
*/
protected $title;
/**
* Keeps current upload comment of an extension's version.
*
* @var string
*/
protected $uploadcomment;
/**
* Keeps current version number.
*
* @var string
*/
protected $version;
/**
* Keeps current download number of an extension's version.
*
* @var string
*/
protected $versionDownloadCounter;
/**
* Returns an assoziative array of all extension version properties.
*
* Valid array keys of returned array are:
* extkey, version, alldownloadcounter, downloadcounter, title, description,
* state, reviewstate, category, lastuploaddate, uploadcomment, dependencies,
* authorname, authoremail, authorcompany, ownerusername, t3xfilemd5
*
* @see $extensionKey, $version, $extensionDownloadCounter,
* @return array assoziative array of an extension version's properties
*/
public function getAll()
{
$versionProperties = [];
$versionProperties['extkey'] = $this->extensionKey;
$versionProperties['version'] = $this->version;
$versionProperties['alldownloadcounter'] = $this->extensionDownloadCounter;
$versionProperties['downloadcounter'] = $this->versionDownloadCounter;
$versionProperties['title'] = $this->title;
$versionProperties['description'] = $this->description;
$versionProperties['state'] = $this->state;
$versionProperties['reviewstate'] = $this->reviewstate;
$versionProperties['category'] = $this->category;
$versionProperties['lastuploaddate'] = $this->lastuploaddate;
$versionProperties['uploadcomment'] = $this->uploadcomment;
$versionProperties['dependencies'] = $this->dependencies;
$versionProperties['authorname'] = $this->authorname;
$versionProperties['authoremail'] = $this->authoremail;
$versionProperties['authorcompany'] = $this->authorcompany;
$versionProperties['ownerusername'] = $this->ownerusername;
$versionProperties['t3xfilemd5'] = $this->t3xfilemd5;
return $versionProperties;
}
/**
* Returns download number sum of all extension's versions.
*
* @return string download number sum
* @see $extensionDLCounter, getAll()
*/
public function getAlldownloadcounter()
{
return $this->extensionDownloadCounter;
}
/**
* Returns company name of extension author.
*
* @return string company name of extension author
* @see $authorcompany, getAll()
*/
public function getAuthorcompany()
{
return $this->authorcompany;
}
/**
* Returns e-mail address of extension author.
*
* @return string e-mail address of extension author
* @see $authoremail, getAll()
*/
public function getAuthoremail()
{
return $this->authoremail;
}
/**
* Returns name of extension author.
*
* @return string name of extension author
* @see $authorname, getAll()
*/
public function getAuthorname()
{
return $this->authorname;
}
/**
* Returns category of an extension.
*
* @return string extension category
* @see $category, getAll()
*/
public function getCategory()
{
return $this->category;
}
/**
* Returns dependencies of an extension's version.
*
* @return string extension dependencies
* @see $dependencies, getAll()
*/
public function getDependencies()
{
return $this->dependencies;
}
/**
* Returns description of an extension's version.
*
* @return string extension description
* @see $description, getAll()
*/
public function getDescription()
{
return $this->description;
}
/**
* Returns download number of an extension's version.
*
* @return string download number
* @see $versionDLCounter, getAll()
*/
public function getDownloadcounter()
{
return $this->versionDownloadCounter;
}
/**
* Returns key of an extension.
*
* @return string extension key
* @see $extensionKey, getAll()
*/
public function getExtkey()
{
return $this->extensionKey;
}
/**
* Returns last uploaddate of an extension's version.
*
* @return string last upload date of an extension's version
* @see $lastuploaddate, getAll()
*/
public function getLastuploaddate()
{
return $this->lastuploaddate;
}
/**
* Returns username of extension owner.
*
* @return string extension owner's username
* @see $ownerusername, getAll()
*/
public function getOwnerusername()
{
return $this->ownerusername;
}
/**
* Returns review state of an extension's version.
*
* @return string extension review state
* @see $reviewstate, getAll()
*/
public function getReviewstate()
{
return $this->reviewstate;
}
/**
* Returns state of an extension's version.
*
* @return string extension state
* @see $state, getAll()
*/
public function getState()
{
return $this->state;
}
/**
* Returns t3x file hash of an extension's version.
*
* @return string t3x file hash
* @see $t3xfilemd5, getAll()
*/
public function getT3xfilemd5()
{
return $this->t3xfilemd5;
}
/**
* Returns title of an extension's version.
*
* @return string extension title
* @see $title, getAll()
*/
public function getTitle()
{
return $this->title;
}
/**
* Returns extension upload comment.
*
* @return string extension upload comment
* @see $uploadcomment, getAll()
*/
public function getUploadcomment()
{
return $this->uploadcomment;
}
/**
* Returns version number.
*
* @return string version number
* @see $version, getAll()
*/
public function getVersion()
{
return $this->version;
}
/**
* Method resets version class properties.
*
* @param bool $resetAll If TRUE, additionally extension properties are reset
* @see $extensionKey, $version, $extensionDLCounter, $versionDLCounter,
*/
protected function resetProperties($resetAll = false)
{
// resetting at least class property "version" is mandatory
// as we need to do some magic in regards to
// an extension's and version's child node "downloadcounter"
$this->version = $this->title = $this->versionDownloadCounter = $this->description = $this->state = $this->reviewstate = $this->category = $this->lastuploaddate = $this->uploadcomment = $this->dependencies = $this->authorname = $this->authoremail = $this->authorcompany = $this->ownerusername = $this->t3xfilemd5 = null;
if ($resetAll) {
$this->extensionKey = $this->extensionDownloadCounter = null;
}
}
/**
* Convert dependencies from TER format to EM_CONF format
*
* @param string $dependencies serialized dependency array
* @return string
*/
protected function convertDependencies($dependencies)
{
$newDependencies = [];
$dependenciesArray = unserialize($dependencies, ['allowed_classes' => false]);
if (is_array($dependenciesArray)) {
foreach ($dependenciesArray as $version) {
if (!empty($version['kind']) && !empty($version['extensionKey'])) {
$newDependencies[$version['kind']][$version['extensionKey']] = $version['versionRange'];
}
}
}
return serialize($newDependencies);
}
}
@@ -0,0 +1,181 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Abstract parser for TYPO3's mirror.xml file.
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
abstract class AbstractMirrorXmlParser extends AbstractXmlParser
{
/**
* Keeps country of currently processed mirror.
*
* @var string
*/
protected $country;
/**
* Keeps hostname of currently processed mirror.
*
* @var string
*/
protected $host;
/**
* Keeps path to mirrored TER of currently processed mirror.
*
* @var string
*/
protected $path;
/**
* Keeps sponsor link of currently processed mirror.
*
* @var string
*/
protected $sponsorlink;
/**
* Keeps sponsor logo location of currently processed mirror.
*
* @var string
*/
protected $sponsorlogo;
/**
* Keeps sponsor name of currently processed mirror.
*
* @var string
*/
protected $sponsorname;
/**
* Keeps title of currently processed mirror.
*
* @var string
*/
protected $title;
/**
* Returns an associative array of all mirror properties.
*
* Valid array keys of returned array are:
* country, host, path, sponsorlink, sponsorlogo, sponsorname, title
*
* @return array assoziative array of a mirror's properties
* @see $country, $host, $path, $sponsorlink, $sponsorlogo, $sponsorname, $title
*/
public function getAll()
{
$mirrorProperties = [];
$mirrorProperties['title'] = $this->title;
$mirrorProperties['host'] = $this->host;
$mirrorProperties['path'] = $this->path;
$mirrorProperties['country'] = $this->country;
$mirrorProperties['sponsorname'] = $this->sponsorname;
$mirrorProperties['sponsorlink'] = $this->sponsorlink;
$mirrorProperties['sponsorlogo'] = $this->sponsorlogo;
return $mirrorProperties;
}
/**
* Returns country of currently processed mirror.
*
* @return string name of country a mirror is located in
* @see $country, getAll()
*/
public function getCountry()
{
return $this->country;
}
/**
* Returns host of currently processed mirror.
*
* @return string host name
* @see $host, getAll()
*/
public function getHost()
{
return $this->host;
}
/**
* Returns path to mirrored TER of currently processed mirror.
*
* @return string path name
* @see $path, getAll()
*/
public function getPath()
{
return $this->path;
}
/**
* Returns sponsor link of currently processed mirror.
*
* @return string URL of a sponsor's website
* @see $sponsorlink, getAll()
*/
public function getSponsorlink()
{
return $this->sponsorlink;
}
/**
* Returns sponsor logo location of currently processed mirror.
*
* @return string a sponsor's logo location
* @see $sponsorlogo, getAll()
*/
public function getSponsorlogo()
{
return $this->sponsorlogo;
}
/**
* Returns sponsor name of currently processed mirror.
*
* @return string name of sponsor
* @see $sponsorname, getAll()
*/
public function getSponsorname()
{
return $this->sponsorname;
}
/**
* Returns title of currently processed mirror.
*
* @return string title of mirror
* @see $title, getAll()
*/
public function getTitle()
{
return $this->title;
}
/**
* Method resets version class properties.
*
* @see $country, $host, $path, $sponsorlink, $sponsorlogo, $sponsorname, $title
*/
protected function resetProperties()
{
$this->title = $this->host = $this->path = $this->country
= $this->sponsorname = $this->sponsorlink = $this->sponsorlogo = null;
}
}
@@ -0,0 +1,113 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Abstract parser for EM related TYPO3 xml files.
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
abstract class AbstractXmlParser implements \SplSubject
{
/**
* Keeps XML parser instance.
*
* @var mixed
*/
protected $objXml;
/**
* Keeps name of required PHP extension
* for this class to work properly.
*
* @var string
*/
protected $requiredPhpExtensions;
/**
* Keeps list of attached observers.
*
* @var \SplObserver[]
*/
protected $observers = [];
/**
* Method attaches an observer.
*
* @param \SplObserver $observer an observer to attach
* @see $observers, detach(), notify()
*/
public function attach(\SplObserver $observer)
{
$this->observers[] = $observer;
}
/**
* Method detaches an attached observer
*
* @param \SplObserver $observer an observer to detach
* @see $observers, attach(), notify()
*/
public function detach(\SplObserver $observer)
{
$key = array_search($observer, $this->observers, true);
if ($key !== false) {
unset($this->observers[$key]);
}
}
/**
* Method notifies attached observers.
*
* @see $observers, attach(), detach()
*/
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
/**
* Method determines if a necessary PHP extension is available.
*
* Method tries to load the extension if necessary and possible.
*
* @return bool TRUE, if PHP extension is available, otherwise FALSE
*/
public function isAvailable()
{
$isAvailable = true;
if (!extension_loaded($this->requiredPhpExtensions)) {
$prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : '';
if (!(((bool)ini_get('enable_dl') && !(bool)ini_get('safe_mode')) && function_exists('dl') && dl($prefix . $this->requiredPhpExtensions . PHP_SHLIB_SUFFIX))) {
$isAvailable = false;
}
}
return $isAvailable;
}
/**
* Method parses an XML file.
*
* @param string $file GZIP stream resource
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of XML parser errors
*/
abstract public function parseXml($file);
/**
* Create required parser
*/
abstract protected function createParser();
}
@@ -0,0 +1,184 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Parser for TYPO3's extension.xml file.
*
* Depends on PHP ext/xmlreader which should be available
* with PHP >= 5.1.0.
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
class ExtensionXmlPullParser extends AbstractExtensionXmlParser
{
/**
* Class constructor.
*/
public function __construct()
{
$this->requiredPhpExtensions = 'xmlreader';
}
/**
* Create required parser
*/
protected function createParser()
{
$this->objXml = new \XMLReader();
}
/**
* Method parses an extensions.xml file.
*
* @param string $file GZIP stream resource
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of parser error
*/
public function parseXml($file)
{
$this->createParser();
if (!(is_object($this->objXml) && get_class($this->objXml) === 'XMLReader')) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640540);
}
if ($this->objXml->open($file, 'utf-8') === false) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(
sprintf('Unable to open file resource %s.', $file),
1476108651
);
}
while ($this->objXml->read()) {
if ($this->objXml->nodeType == \XMLReader::ELEMENT) {
$this->startElement($this->objXml->name);
} else {
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT) {
$this->endElement($this->objXml->name);
} else {
continue;
}
}
}
$this->objXml->close();
}
/**
* Method is invoked when parser accesses start tag of an element.
*
* @param string $elementName element name at parser's current position
*/
protected function startElement($elementName)
{
switch ($elementName) {
case 'extension':
$this->extensionKey = $this->objXml->getAttribute('extensionkey');
break;
case 'version':
$this->version = $this->objXml->getAttribute('version');
break;
case 'downloadcounter':
// downloadcounter could be a child node of
// extension or version
if ($this->version == null) {
$this->extensionDownloadCounter = $this->getElementValue($elementName);
} else {
$this->versionDownloadCounter = $this->getElementValue($elementName);
}
break;
case 'title':
$this->title = $this->getElementValue($elementName);
break;
case 'description':
$this->description = $this->getElementValue($elementName);
break;
case 'state':
$this->state = $this->getElementValue($elementName);
break;
case 'reviewstate':
$this->reviewstate = $this->getElementValue($elementName);
break;
case 'category':
$this->category = $this->getElementValue($elementName);
break;
case 'lastuploaddate':
$this->lastuploaddate = $this->getElementValue($elementName);
break;
case 'uploadcomment':
$this->uploadcomment = $this->getElementValue($elementName);
break;
case 'dependencies':
$this->dependencies = $this->convertDependencies($this->getElementValue($elementName));
break;
case 'authorname':
$this->authorname = $this->getElementValue($elementName);
break;
case 'authoremail':
$this->authoremail = $this->getElementValue($elementName);
break;
case 'authorcompany':
$this->authorcompany = $this->getElementValue($elementName);
break;
case 'ownerusername':
$this->ownerusername = $this->getElementValue($elementName);
break;
case 't3xfilemd5':
$this->t3xfilemd5 = $this->getElementValue($elementName);
break;
}
}
/**
* Method is invoked when parser accesses end tag of an element.
*
* @param string $elementName: element name at parser's current position
*/
protected function endElement($elementName)
{
switch ($elementName) {
case 'extension':
$this->resetProperties(true);
break;
case 'version':
$this->notify();
$this->resetProperties();
break;
}
}
/**
* Method returns the value of an element at XMLReader's current
* position.
*
* Method will read until it finds the end of the given element.
* If element has no value, method returns NULL.
*
* @param string $elementName: name of element to retrieve it's value from
* @return string an element's value if it has a value, otherwise NULL
*/
protected function getElementValue(&$elementName)
{
$value = null;
if (!$this->objXml->isEmptyElement) {
$value = '';
while ($this->objXml->read()) {
if ($this->objXml->nodeType == \XMLReader::TEXT || $this->objXml->nodeType == \XMLReader::CDATA || $this->objXml->nodeType == \XMLReader::WHITESPACE || $this->objXml->nodeType == \XMLReader::SIGNIFICANT_WHITESPACE) {
$value .= $this->objXml->value;
} else {
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT && $this->objXml->name === $elementName) {
break;
}
}
}
}
return $value;
}
}
@@ -0,0 +1,184 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Parser for TYPO3's extension.xml file.
*
* Depends on PHP ext/xml which should be available
* with PHP 4+. This is the parser used in TYPO3
* Core <= 4.3 (without the "collect all data in one
* array" behaviour).
* Notice: ext/xml has proven to be buggy with entities.
* Use at least PHP 5.2.9+ and libxml2 2.7.3+!
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
class ExtensionXmlPushParser extends AbstractExtensionXmlParser
{
/**
* Keeps current data of element to process.
*
* @var string
*/
protected $elementData = '';
/**
* Class constructor.
*/
public function __construct()
{
$this->requiredPhpExtensions = 'xml';
}
/**
* Create required parser
*/
protected function createParser()
{
$this->objXml = xml_parser_create();
xml_set_object($this->objXml, $this);
}
/**
* Method parses an extensions.xml file.
*
* @param string $file GZIP stream resource
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of parse errors
*/
public function parseXml($file)
{
$this->createParser();
if (!is_resource($this->objXml)) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640663);
}
// Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
$previousValueOfEntityLoader = libxml_disable_entity_loader(true);
// keep original character case of XML document
xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, false);
xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, false);
xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
xml_set_element_handler($this->objXml, 'startElement', 'endElement');
xml_set_character_data_handler($this->objXml, 'characterData');
if (!($fp = fopen($file, 'r'))) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342640689);
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($this->objXml, $data, feof($fp))) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('XML error %s in line %u of file resource %s.', xml_error_string(xml_get_error_code($this->objXml)), xml_get_current_line_number($this->objXml), $file), 1342640703);
}
}
libxml_disable_entity_loader($previousValueOfEntityLoader);
xml_parser_free($this->objXml);
}
/**
* Method is invoked when parser accesses start tag of an element.
*
* @param resource $parser parser resource
* @param string $elementName element name at parser's current position
* @param array $attrs array of an element's attributes if available
*/
protected function startElement($parser, $elementName, $attrs)
{
switch ($elementName) {
case 'extension':
$this->extensionKey = $attrs['extensionkey'];
break;
case 'version':
$this->version = $attrs['version'];
break;
default:
$this->elementData = '';
}
}
/**
* Method is invoked when parser accesses end tag of an element.
*
* @param resource $parser parser resource
* @param string $elementName Element name at parser's current position
*/
protected function endElement($parser, $elementName)
{
switch ($elementName) {
case 'extension':
$this->resetProperties(true);
break;
case 'version':
$this->notify();
$this->resetProperties();
break;
case 'downloadcounter':
// downloadcounter could be a child node of
// extension or version
if ($this->version == null) {
$this->extensionDownloadCounter = $this->elementData;
} else {
$this->versionDownloadCounter = $this->elementData;
}
break;
case 'title':
$this->title = $this->elementData;
break;
case 'description':
$this->description = $this->elementData;
break;
case 'state':
$this->state = $this->elementData;
break;
case 'reviewstate':
$this->reviewstate = $this->elementData;
break;
case 'category':
$this->category = $this->elementData;
break;
case 'lastuploaddate':
$this->lastuploaddate = $this->elementData;
break;
case 'uploadcomment':
$this->uploadcomment = $this->elementData;
break;
case 'dependencies':
$this->dependencies = $this->convertDependencies($this->elementData);
break;
case 'authorname':
$this->authorname = $this->elementData;
break;
case 'authoremail':
$this->authoremail = $this->elementData;
break;
case 'authorcompany':
$this->authorcompany = $this->elementData;
break;
case 'ownerusername':
$this->ownerusername = $this->elementData;
break;
case 't3xfilemd5':
$this->t3xfilemd5 = $this->elementData;
break;
}
}
/**
* Method is invoked when parser accesses any character other than elements.
*
* @param resource $parser parser resource
* @param string $data An element's value
*/
protected function characterData($parser, $data)
{
$this->elementData .= $data;
}
}
@@ -0,0 +1,148 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Parser for TYPO3's mirrors.xml file.
*
* Depends on PHP ext/xmlreader which should be available
* with PHP >= 5.1.0.
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
class MirrorXmlPullParser extends AbstractMirrorXmlParser
{
public function __construct()
{
$this->requiredPhpExtensions = 'xmlreader';
}
/**
* Create required parser
*/
protected function createParser()
{
$this->objXml = new \XMLReader();
}
/**
* Method parses an extensions.xml file.
*
* @param string $file file resource, typically a stream
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of XML parser errors
*/
public function parseXml($file)
{
$this->createParser();
if (!(is_object($this->objXml) && get_class($this->objXml) === 'XMLReader')) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342640820);
}
if ($this->objXml->open($file, 'utf-8') === false) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342640893);
}
while ($this->objXml->read()) {
if ($this->objXml->nodeType == \XMLReader::ELEMENT) {
$this->startElement($this->objXml->name);
} else {
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT) {
$this->endElement($this->objXml->name);
} else {
continue;
}
}
}
$this->objXml->close();
}
/**
* Method is invoked when parser accesses start tag of an element.
*
* @param string $elementName element name at parser's current position
* @see endElement()
*/
protected function startElement($elementName)
{
switch ($elementName) {
case 'title':
$this->title = $this->getElementValue($elementName);
break;
case 'host':
$this->host = $this->getElementValue($elementName);
break;
case 'path':
$this->path = $this->getElementValue($elementName);
break;
case 'country':
$this->country = $this->getElementValue($elementName);
break;
case 'name':
$this->sponsorname = $this->getElementValue($elementName);
break;
case 'link':
$this->sponsorlink = $this->getElementValue($elementName);
break;
case 'logo':
$this->sponsorlogo = $this->getElementValue($elementName);
break;
default:
// Do nothing
}
}
/**
* Method is invoked when parser accesses end tag of an element.
*
* @param string $elementName element name at parser's current position
* @see startElement()
*/
protected function endElement($elementName)
{
switch ($elementName) {
case 'mirror':
$this->notify();
$this->resetProperties();
break;
default:
// Do nothing
}
}
/**
* Method returns the value of an element at XMLReader's current
* position.
*
* Method will read until it finds the end of the given element.
* If element has no value, method returns NULL.
*
* @param string &$elementName name of element to retrieve it's value from
* @return string an element's value if it has a value, otherwise NULL
*/
protected function getElementValue(&$elementName)
{
$value = null;
if (!$this->objXml->isEmptyElement) {
$value = '';
while ($this->objXml->read()) {
if ($this->objXml->nodeType == \XMLReader::TEXT || $this->objXml->nodeType == \XMLReader::CDATA || $this->objXml->nodeType == \XMLReader::WHITESPACE || $this->objXml->nodeType == \XMLReader::SIGNIFICANT_WHITESPACE) {
$value .= $this->objXml->value;
} else {
if ($this->objXml->nodeType == \XMLReader::END_ELEMENT && $this->objXml->name === $elementName) {
break;
}
}
}
}
return $value;
}
}
@@ -0,0 +1,159 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Parser for TYPO3's mirrors.xml file.
*
* Depends on PHP ext/xml which should be available
* with PHP 4+. This is the parser used in TYPO3
* Core <= 4.3 (without the "collect all data in one
* array" behaviour).
* Notice: ext/xml has proven to be buggy with entities.
* Use at least PHP 5.2.9+ and libxml2 2.7.3+!
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
class MirrorXmlPushParser extends AbstractMirrorXmlParser
{
/**
* @var string
*/
protected $element;
/**
* Class constructor.
*/
public function __construct()
{
$this->requiredPhpExtensions = 'xml';
}
/**
* Create required parser
*/
protected function createParser()
{
$this->objXml = xml_parser_create();
xml_set_object($this->objXml, $this);
}
/**
* Method parses a mirror.xml file.
*
* @param string $file GZIP stream resource
* @throws \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException in case of XML parser errors
*/
public function parseXml($file)
{
$this->createParser();
if (!is_resource($this->objXml)) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException('Unable to create XML parser.', 1342641009);
}
// Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
$previousValueOfEntityLoader = libxml_disable_entity_loader(true);
// keep original character case of XML document
xml_parser_set_option($this->objXml, XML_OPTION_CASE_FOLDING, false);
xml_parser_set_option($this->objXml, XML_OPTION_SKIP_WHITE, false);
xml_parser_set_option($this->objXml, XML_OPTION_TARGET_ENCODING, 'utf-8');
xml_set_element_handler($this->objXml, 'startElement', 'endElement');
xml_set_character_data_handler($this->objXml, 'characterData');
if (!($fp = fopen($file, 'r'))) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('Unable to open file resource %s.', $file), 1342641010);
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($this->objXml, $data, feof($fp))) {
throw new \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException(sprintf('XML error %s in line %u of file resource %s.', xml_error_string(xml_get_error_code($this->objXml)), xml_get_current_line_number($this->objXml), $file), 1342641011);
}
}
libxml_disable_entity_loader($previousValueOfEntityLoader);
xml_parser_free($this->objXml);
}
/**
* Method is invoked when parser accesses start tag of an element.
*
* @param resource $parser parser resource
* @param string $elementName element name at parser's current position
* @param array $attrs array of an element's attributes if available
*/
protected function startElement($parser, $elementName, $attrs)
{
switch ($elementName) {
default:
$this->element = $elementName;
}
}
/**
* Method is invoked when parser accesses end tag of an element.
* Although the first parameter seems unused, it needs to be there for
* adherence to the API of xml_set_element_handler
*
* @see xml_set_element_handler
* @param resource $parser parser resource
* @param string $elementName element name at parser's current position
*/
protected function endElement($parser, $elementName)
{
switch ($elementName) {
case 'mirror':
$this->notify();
$this->resetProperties();
break;
default:
$this->element = null;
}
}
/**
* Method is invoked when parser accesses any character other than elements.
* Although the first parameter seems unused, it needs to be there for
* adherence to the API of xml_set_character_data_handler
*
* @see xml_set_character_data_handler
* @param resource $parser parser resource
* @param string $data an element's value
*/
protected function characterData($parser, $data)
{
if (isset($this->element)) {
switch ($this->element) {
case 'title':
$this->title = $data;
break;
case 'host':
$this->host = $data;
break;
case 'path':
$this->path = $data;
break;
case 'country':
$this->country = $data;
break;
case 'name':
$this->sponsorname = $data;
break;
case 'link':
$this->sponsorlink = $data;
break;
case 'logo':
$this->sponsorlogo = $data;
break;
default:
// Do nothing
}
}
}
}
@@ -0,0 +1,78 @@
<?php
namespace TYPO3\CMS\Extensionmanager\Utility\Parser;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Factory for XML parsers.
* @internal This class is a specific ExtensionManager implementation and is not part of the Public TYPO3 API.
*/
class XmlParserFactory
{
/**
* An array with instances of xml parsers.
* This member is set in the getParserInstance() function.
*
* @var array
*/
protected static $instance = [];
/**
* Keeps array of all available parsers.
*
* @todo This would better be moved to a global configuration array like
* $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']. (might require EM to be moved in a sysext)
*
* @var array
*/
protected static $parsers = [
'extension' => [
\TYPO3\CMS\Extensionmanager\Utility\Parser\ExtensionXmlPushParser::class => 'ExtensionXmlPushParser.php',
\TYPO3\CMS\Extensionmanager\Utility\Parser\ExtensionXmlPullParser::class => 'ExtensionXmlPullParser.php',
],
'mirror' => [
\TYPO3\CMS\Extensionmanager\Utility\Parser\MirrorXmlPushParser::class => 'MirrorXmlPushParser.php',
\TYPO3\CMS\Extensionmanager\Utility\Parser\MirrorXmlPullParser::class=> 'MirrorXmlPullParser.php',
]
];
/**
* Obtains a xml parser instance.
*
* This function will return an instance of a class that implements
* \TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractExtensionXmlParser
*
* @param string $parserType type of parser, one of extension and mirror
* @param string $excludeClassNames (optional) comma-separated list of class names
* @return \TYPO3\CMS\Extensionmanager\Utility\Parser\AbstractExtensionXmlParser an instance of an extension.xml parser
*/
public static function getParserInstance($parserType, $excludeClassNames = '')
{
if (!isset(self::$instance[$parserType]) || !is_object(self::$instance[$parserType]) || !empty($excludeClassNames)) {
// reset instance
self::$instance[$parserType] = ($objParser = null);
foreach (self::$parsers[$parserType] as $className => $file) {
if (!\TYPO3\CMS\Core\Utility\GeneralUtility::inList($excludeClassNames, $className)) {
$objParser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($className);
if ($objParser->isAvailable()) {
self::$instance[$parserType] = &$objParser;
break;
}
$objParser = null;
}
}
}
return self::$instance[$parserType];
}
}