1cf2dcf1bSAndreas Gohr<?php 2cf2dcf1bSAndreas Gohr 3cf2dcf1bSAndreas Gohrnamespace dokuwiki\plugin\extension; 4cf2dcf1bSAndreas Gohr 5cf2dcf1bSAndreas Gohruse dokuwiki\Extension\PluginController; 6cf2dcf1bSAndreas Gohruse dokuwiki\Utf8\PhpString; 7cf2dcf1bSAndreas Gohruse RuntimeException; 8cf2dcf1bSAndreas Gohr 9cf2dcf1bSAndreas Gohrclass Extension 10cf2dcf1bSAndreas Gohr{ 117c184cfcSAndreas Gohr public const TYPE_PLUGIN = 'plugin'; 127c184cfcSAndreas Gohr public const TYPE_TEMPLATE = 'template'; 13cf2dcf1bSAndreas Gohr 143e63733dSAndreas Gohr /** @var string[] The types the API uses for plugin components */ 153e63733dSAndreas Gohr public const COMPONENT_TYPES = [ 163e63733dSAndreas Gohr 1 => 'Syntax', 173e63733dSAndreas Gohr 2 => 'Admin', 183e63733dSAndreas Gohr 4 => 'Action', 193e63733dSAndreas Gohr 8 => 'Render', 203e63733dSAndreas Gohr 16 => 'Helper', 213e63733dSAndreas Gohr 32 => 'Template', 223e63733dSAndreas Gohr 64 => 'Remote', 233e63733dSAndreas Gohr 128 => 'Auth', 243e63733dSAndreas Gohr 256 => 'CLI', 253e63733dSAndreas Gohr 512 => 'CSS/JS-only', 263e63733dSAndreas Gohr ]; 273e63733dSAndreas Gohr 28cf2dcf1bSAndreas Gohr /** @var string "plugin"|"template" */ 29cf2dcf1bSAndreas Gohr protected string $type = self::TYPE_PLUGIN; 30cf2dcf1bSAndreas Gohr 31cf2dcf1bSAndreas Gohr /** @var string The base name of this extension */ 32cf2dcf1bSAndreas Gohr protected string $base; 33cf2dcf1bSAndreas Gohr 3425d28a01SAndreas Gohr /** @var string The current location of this extension */ 3525d28a01SAndreas Gohr protected string $currentDir = ''; 36cf2dcf1bSAndreas Gohr 37cf2dcf1bSAndreas Gohr /** @var array The local info array of the extension */ 38cf2dcf1bSAndreas Gohr protected array $localInfo = []; 39cf2dcf1bSAndreas Gohr 40cf2dcf1bSAndreas Gohr /** @var array The remote info array of the extension */ 41cf2dcf1bSAndreas Gohr protected array $remoteInfo = []; 42cf2dcf1bSAndreas Gohr 437c9966a5SAndreas Gohr /** @var Manager|null The manager for this extension */ 4425d28a01SAndreas Gohr protected ?Manager $manager = null; 45cf2dcf1bSAndreas Gohr 46cf2dcf1bSAndreas Gohr // region Constructors 47cf2dcf1bSAndreas Gohr 48cf2dcf1bSAndreas Gohr /** 49cf2dcf1bSAndreas Gohr * The main constructor is private to force the use of the factory methods 50cf2dcf1bSAndreas Gohr */ 51cf2dcf1bSAndreas Gohr protected function __construct() 52cf2dcf1bSAndreas Gohr { 53cf2dcf1bSAndreas Gohr } 54cf2dcf1bSAndreas Gohr 55cf2dcf1bSAndreas Gohr /** 56a1e045f7SAndreas Gohr * Initializes an extension from an id 57a1e045f7SAndreas Gohr * 58a1e045f7SAndreas Gohr * @param string $id The id of the extension 59a1e045f7SAndreas Gohr * @return Extension 60a1e045f7SAndreas Gohr */ 61a1e045f7SAndreas Gohr public static function createFromId($id) 62a1e045f7SAndreas Gohr { 63a1e045f7SAndreas Gohr $extension = new self(); 64a1e045f7SAndreas Gohr $extension->initFromId($id); 65a1e045f7SAndreas Gohr return $extension; 66a1e045f7SAndreas Gohr } 67a1e045f7SAndreas Gohr 68a1e045f7SAndreas Gohr protected function initFromId($id) 69a1e045f7SAndreas Gohr { 70a1e045f7SAndreas Gohr [$type, $base] = $this->idToTypeBase($id); 71a1e045f7SAndreas Gohr $this->type = $type; 72a1e045f7SAndreas Gohr $this->base = $base; 73a1e045f7SAndreas Gohr $this->readLocalInfo(); 74a1e045f7SAndreas Gohr } 75a1e045f7SAndreas Gohr 76a1e045f7SAndreas Gohr /** 77cf2dcf1bSAndreas Gohr * Initializes an extension from a directory 78cf2dcf1bSAndreas Gohr * 79cf2dcf1bSAndreas Gohr * The given directory might be the one where the extension has already been installed to 80cf2dcf1bSAndreas Gohr * or it might be the extracted source in some temporary directory. 81cf2dcf1bSAndreas Gohr * 82cf2dcf1bSAndreas Gohr * @param string $dir Where the extension code is currently located 83cf2dcf1bSAndreas Gohr * @param string|null $type TYPE_PLUGIN|TYPE_TEMPLATE, null for auto-detection 84cf2dcf1bSAndreas Gohr * @param string $base The base name of the extension, null for auto-detection 85cf2dcf1bSAndreas Gohr * @return Extension 86cf2dcf1bSAndreas Gohr */ 87cf2dcf1bSAndreas Gohr public static function createFromDirectory($dir, $type = null, $base = null) 88cf2dcf1bSAndreas Gohr { 89cf2dcf1bSAndreas Gohr $extension = new self(); 90cf2dcf1bSAndreas Gohr $extension->initFromDirectory($dir, $type, $base); 91cf2dcf1bSAndreas Gohr return $extension; 92cf2dcf1bSAndreas Gohr } 93cf2dcf1bSAndreas Gohr 94cf2dcf1bSAndreas Gohr protected function initFromDirectory($dir, $type = null, $base = null) 95cf2dcf1bSAndreas Gohr { 96cf2dcf1bSAndreas Gohr if (!is_dir($dir)) throw new RuntimeException('Directory not found: ' . $dir); 97e8fd67e9SAndreas Gohr $this->currentDir = fullpath($dir); 98cf2dcf1bSAndreas Gohr 99cf2dcf1bSAndreas Gohr if ($type === null || $type === self::TYPE_TEMPLATE) { 100cf2dcf1bSAndreas Gohr if ( 1018fe483c9SAndreas Gohr file_exists($dir . '/template.info.txt') || 102cf2dcf1bSAndreas Gohr file_exists($dir . '/style.ini') || 103cf2dcf1bSAndreas Gohr file_exists($dir . '/main.php') || 104cf2dcf1bSAndreas Gohr file_exists($dir . '/detail.php') || 105cf2dcf1bSAndreas Gohr file_exists($dir . '/mediamanager.php') 106cf2dcf1bSAndreas Gohr ) { 107cf2dcf1bSAndreas Gohr $this->type = self::TYPE_TEMPLATE; 108cf2dcf1bSAndreas Gohr } 109cf2dcf1bSAndreas Gohr } else { 110cf2dcf1bSAndreas Gohr $this->type = self::TYPE_PLUGIN; 111cf2dcf1bSAndreas Gohr } 112cf2dcf1bSAndreas Gohr 113cf2dcf1bSAndreas Gohr $this->readLocalInfo(); 114cf2dcf1bSAndreas Gohr 115cf2dcf1bSAndreas Gohr if ($base !== null) { 116cf2dcf1bSAndreas Gohr $this->base = $base; 117cf2dcf1bSAndreas Gohr } elseif (isset($this->localInfo['base'])) { 118cf2dcf1bSAndreas Gohr $this->base = $this->localInfo['base']; 119cf2dcf1bSAndreas Gohr } else { 120cf2dcf1bSAndreas Gohr $this->base = basename($dir); 121cf2dcf1bSAndreas Gohr } 122cf2dcf1bSAndreas Gohr } 123cf2dcf1bSAndreas Gohr 124cf2dcf1bSAndreas Gohr /** 125cf2dcf1bSAndreas Gohr * Initializes an extension from remote data 126cf2dcf1bSAndreas Gohr * 127cf2dcf1bSAndreas Gohr * @param array $data The data as returned by the repository api 128cf2dcf1bSAndreas Gohr * @return Extension 129cf2dcf1bSAndreas Gohr */ 130cf2dcf1bSAndreas Gohr public static function createFromRemoteData($data) 131cf2dcf1bSAndreas Gohr { 132cf2dcf1bSAndreas Gohr $extension = new self(); 133cf2dcf1bSAndreas Gohr $extension->initFromRemoteData($data); 134cf2dcf1bSAndreas Gohr return $extension; 135cf2dcf1bSAndreas Gohr } 136cf2dcf1bSAndreas Gohr 137cf2dcf1bSAndreas Gohr protected function initFromRemoteData($data) 138cf2dcf1bSAndreas Gohr { 139cf2dcf1bSAndreas Gohr if (!isset($data['plugin'])) throw new RuntimeException('Invalid remote data'); 140cf2dcf1bSAndreas Gohr 141a1e045f7SAndreas Gohr [$type, $base] = $this->idToTypeBase($data['plugin']); 142cf2dcf1bSAndreas Gohr $this->remoteInfo = $data; 143cf2dcf1bSAndreas Gohr $this->type = $type; 144cf2dcf1bSAndreas Gohr $this->base = $base; 145cf2dcf1bSAndreas Gohr 146cf2dcf1bSAndreas Gohr if ($this->isInstalled()) { 147cf2dcf1bSAndreas Gohr $this->currentDir = $this->getInstallDir(); 148cf2dcf1bSAndreas Gohr $this->readLocalInfo(); 149cf2dcf1bSAndreas Gohr } 150cf2dcf1bSAndreas Gohr } 151cf2dcf1bSAndreas Gohr 152cf2dcf1bSAndreas Gohr // endregion 153cf2dcf1bSAndreas Gohr 154cf2dcf1bSAndreas Gohr // region Getters 155cf2dcf1bSAndreas Gohr 156cf2dcf1bSAndreas Gohr /** 1574fd6a1d7SAndreas Gohr * @param bool $wrap If true, the id is wrapped in backticks 158cf2dcf1bSAndreas Gohr * @return string The extension id (same as base but prefixed with "template:" for templates) 159cf2dcf1bSAndreas Gohr */ 1604fd6a1d7SAndreas Gohr public function getId($wrap = false) 161cf2dcf1bSAndreas Gohr { 162cf2dcf1bSAndreas Gohr if ($this->type === self::TYPE_TEMPLATE) { 1634fd6a1d7SAndreas Gohr $id = self::TYPE_TEMPLATE . ':' . $this->base; 1644fd6a1d7SAndreas Gohr } else { 1654fd6a1d7SAndreas Gohr $id = $this->base; 166cf2dcf1bSAndreas Gohr } 1674fd6a1d7SAndreas Gohr if ($wrap) $id = "`$id`"; 1684fd6a1d7SAndreas Gohr return $id; 169cf2dcf1bSAndreas Gohr } 170cf2dcf1bSAndreas Gohr 171cf2dcf1bSAndreas Gohr /** 172cf2dcf1bSAndreas Gohr * Get the base name of this extension 173cf2dcf1bSAndreas Gohr * 174cf2dcf1bSAndreas Gohr * @return string 175cf2dcf1bSAndreas Gohr */ 176cf2dcf1bSAndreas Gohr public function getBase() 177cf2dcf1bSAndreas Gohr { 178cf2dcf1bSAndreas Gohr return $this->base; 179cf2dcf1bSAndreas Gohr } 180cf2dcf1bSAndreas Gohr 181cf2dcf1bSAndreas Gohr /** 182cf2dcf1bSAndreas Gohr * Get the type of the extension 183cf2dcf1bSAndreas Gohr * 184cf2dcf1bSAndreas Gohr * @return string "plugin"|"template" 185cf2dcf1bSAndreas Gohr */ 186cf2dcf1bSAndreas Gohr public function getType() 187cf2dcf1bSAndreas Gohr { 188cf2dcf1bSAndreas Gohr return $this->type; 189cf2dcf1bSAndreas Gohr } 190cf2dcf1bSAndreas Gohr 191cf2dcf1bSAndreas Gohr /** 192cf2dcf1bSAndreas Gohr * The current directory of the extension 193cf2dcf1bSAndreas Gohr * 194cf2dcf1bSAndreas Gohr * @return string|null 195cf2dcf1bSAndreas Gohr */ 196cf2dcf1bSAndreas Gohr public function getCurrentDir() 197cf2dcf1bSAndreas Gohr { 198cf2dcf1bSAndreas Gohr // recheck that the current currentDir is still valid 199cf2dcf1bSAndreas Gohr if ($this->currentDir && !is_dir($this->currentDir)) { 20025d28a01SAndreas Gohr $this->currentDir = ''; 201cf2dcf1bSAndreas Gohr } 202cf2dcf1bSAndreas Gohr 203cf2dcf1bSAndreas Gohr // if the extension is installed, then the currentDir is the install dir! 204cf2dcf1bSAndreas Gohr if (!$this->currentDir && $this->isInstalled()) { 205cf2dcf1bSAndreas Gohr $this->currentDir = $this->getInstallDir(); 206cf2dcf1bSAndreas Gohr } 207cf2dcf1bSAndreas Gohr 208cf2dcf1bSAndreas Gohr return $this->currentDir; 209cf2dcf1bSAndreas Gohr } 210cf2dcf1bSAndreas Gohr 211cf2dcf1bSAndreas Gohr /** 212cf2dcf1bSAndreas Gohr * Get the directory where this extension should be installed in 213cf2dcf1bSAndreas Gohr * 214cf2dcf1bSAndreas Gohr * Note: this does not mean that the extension is actually installed there 215cf2dcf1bSAndreas Gohr * 216cf2dcf1bSAndreas Gohr * @return string 217cf2dcf1bSAndreas Gohr */ 218cf2dcf1bSAndreas Gohr public function getInstallDir() 219cf2dcf1bSAndreas Gohr { 220cf2dcf1bSAndreas Gohr if ($this->isTemplate()) { 221176901c2SAndreas Gohr $dir = dirname(tpl_incdir()) . '/' . $this->base; 222cf2dcf1bSAndreas Gohr } else { 223cf2dcf1bSAndreas Gohr $dir = DOKU_PLUGIN . $this->base; 224cf2dcf1bSAndreas Gohr } 225cf2dcf1bSAndreas Gohr 22625d28a01SAndreas Gohr return fullpath($dir); 227cf2dcf1bSAndreas Gohr } 228cf2dcf1bSAndreas Gohr 229cf2dcf1bSAndreas Gohr 230cf2dcf1bSAndreas Gohr /** 231cf2dcf1bSAndreas Gohr * Get the display name of the extension 232cf2dcf1bSAndreas Gohr * 233cf2dcf1bSAndreas Gohr * @return string 234cf2dcf1bSAndreas Gohr */ 235cf2dcf1bSAndreas Gohr public function getDisplayName() 236cf2dcf1bSAndreas Gohr { 237cf2dcf1bSAndreas Gohr return $this->getTag('name', PhpString::ucwords($this->getBase() . ' ' . $this->getType())); 238cf2dcf1bSAndreas Gohr } 239cf2dcf1bSAndreas Gohr 240cf2dcf1bSAndreas Gohr /** 241cf2dcf1bSAndreas Gohr * Get the author name of the extension 242cf2dcf1bSAndreas Gohr * 243cf2dcf1bSAndreas Gohr * @return string Returns an empty string if the author info is missing 244cf2dcf1bSAndreas Gohr */ 245cf2dcf1bSAndreas Gohr public function getAuthor() 246cf2dcf1bSAndreas Gohr { 247cf2dcf1bSAndreas Gohr return $this->getTag('author'); 248cf2dcf1bSAndreas Gohr } 249cf2dcf1bSAndreas Gohr 250cf2dcf1bSAndreas Gohr /** 251cf2dcf1bSAndreas Gohr * Get the email of the author of the extension if there is any 252cf2dcf1bSAndreas Gohr * 253cf2dcf1bSAndreas Gohr * @return string Returns an empty string if the email info is missing 254cf2dcf1bSAndreas Gohr */ 255cf2dcf1bSAndreas Gohr public function getEmail() 256cf2dcf1bSAndreas Gohr { 257cf2dcf1bSAndreas Gohr // email is only in the local data 258cf2dcf1bSAndreas Gohr return $this->localInfo['email'] ?? ''; 259cf2dcf1bSAndreas Gohr } 260cf2dcf1bSAndreas Gohr 261cf2dcf1bSAndreas Gohr /** 262cf2dcf1bSAndreas Gohr * Get the email id, i.e. the md5sum of the email 263cf2dcf1bSAndreas Gohr * 264cf2dcf1bSAndreas Gohr * @return string Empty string if no email is available 265cf2dcf1bSAndreas Gohr */ 266cf2dcf1bSAndreas Gohr public function getEmailID() 267cf2dcf1bSAndreas Gohr { 268cf2dcf1bSAndreas Gohr if (!empty($this->remoteInfo['emailid'])) return $this->remoteInfo['emailid']; 269cf2dcf1bSAndreas Gohr if (!empty($this->localInfo['email'])) return md5($this->localInfo['email']); 270cf2dcf1bSAndreas Gohr return ''; 271cf2dcf1bSAndreas Gohr } 272cf2dcf1bSAndreas Gohr 273cf2dcf1bSAndreas Gohr /** 274cf2dcf1bSAndreas Gohr * Get the description of the extension 275cf2dcf1bSAndreas Gohr * 276cf2dcf1bSAndreas Gohr * @return string Empty string if no description is available 277cf2dcf1bSAndreas Gohr */ 278cf2dcf1bSAndreas Gohr public function getDescription() 279cf2dcf1bSAndreas Gohr { 280cf2dcf1bSAndreas Gohr return $this->getTag(['desc', 'description']); 281cf2dcf1bSAndreas Gohr } 282cf2dcf1bSAndreas Gohr 283cf2dcf1bSAndreas Gohr /** 284cf2dcf1bSAndreas Gohr * Get the URL of the extension, usually a page on dokuwiki.org 285cf2dcf1bSAndreas Gohr * 286cf2dcf1bSAndreas Gohr * @return string 287cf2dcf1bSAndreas Gohr */ 288cf2dcf1bSAndreas Gohr public function getURL() 289cf2dcf1bSAndreas Gohr { 290cf2dcf1bSAndreas Gohr return $this->getTag( 291cf2dcf1bSAndreas Gohr 'url', 292cf2dcf1bSAndreas Gohr 'https://www.dokuwiki.org/' . 293cf2dcf1bSAndreas Gohr ($this->isTemplate() ? 'template' : 'plugin') . ':' . $this->getBase() 294cf2dcf1bSAndreas Gohr ); 295cf2dcf1bSAndreas Gohr } 296cf2dcf1bSAndreas Gohr 297cf2dcf1bSAndreas Gohr /** 2987c9966a5SAndreas Gohr * Get the version of the extension that is actually installed 2997c9966a5SAndreas Gohr * 3007c9966a5SAndreas Gohr * Returns an empty string if the version is not available 3017c9966a5SAndreas Gohr * 3027c9966a5SAndreas Gohr * @return string 3037c9966a5SAndreas Gohr */ 3047c9966a5SAndreas Gohr public function getInstalledVersion() 3057c9966a5SAndreas Gohr { 3067c9966a5SAndreas Gohr return $this->localInfo['date'] ?? ''; 3077c9966a5SAndreas Gohr } 3087c9966a5SAndreas Gohr 3097c9966a5SAndreas Gohr /** 3104fd6a1d7SAndreas Gohr * Get the types of components this extension provides 3114fd6a1d7SAndreas Gohr * 3124fd6a1d7SAndreas Gohr * @return array int -> type 3134fd6a1d7SAndreas Gohr */ 3144fd6a1d7SAndreas Gohr public function getComponentTypes() 3154fd6a1d7SAndreas Gohr { 3168fe483c9SAndreas Gohr // for installed extensions we can check the files 3178fe483c9SAndreas Gohr if ($this->isInstalled()) { 3188fe483c9SAndreas Gohr if ($this->isTemplate()) { 3198fe483c9SAndreas Gohr return ['Template']; 3208fe483c9SAndreas Gohr } else { 3218fe483c9SAndreas Gohr $types = []; 3223e63733dSAndreas Gohr foreach (self::COMPONENT_TYPES as $type) { 3238fe483c9SAndreas Gohr $check = strtolower($type); 3248fe483c9SAndreas Gohr if ( 3258fe483c9SAndreas Gohr file_exists($this->getInstallDir() . '/' . $check . '.php') || 3268fe483c9SAndreas Gohr is_dir($this->getInstallDir() . '/' . $check) 3278fe483c9SAndreas Gohr ) { 3288fe483c9SAndreas Gohr $types[] = $type; 3298fe483c9SAndreas Gohr } 3308fe483c9SAndreas Gohr } 3318fe483c9SAndreas Gohr return $types; 3328fe483c9SAndreas Gohr } 3338fe483c9SAndreas Gohr } 3348fe483c9SAndreas Gohr // still, here? use the remote info 3354fd6a1d7SAndreas Gohr return $this->getTag('types', []); 3364fd6a1d7SAndreas Gohr } 3374fd6a1d7SAndreas Gohr 3384fd6a1d7SAndreas Gohr /** 33925d28a01SAndreas Gohr * Get a list of extension ids this extension depends on 34025d28a01SAndreas Gohr * 34125d28a01SAndreas Gohr * @return string[] 34225d28a01SAndreas Gohr */ 34325d28a01SAndreas Gohr public function getDependencyList() 34425d28a01SAndreas Gohr { 34525d28a01SAndreas Gohr return $this->getTag('depends', []); 34625d28a01SAndreas Gohr } 34725d28a01SAndreas Gohr 34825d28a01SAndreas Gohr /** 349b69d74f1SAndreas Gohr * Get a list of extensions that are currently installed, enabled and depend on this extension 350b69d74f1SAndreas Gohr * 351b69d74f1SAndreas Gohr * @return Extension[] 352b69d74f1SAndreas Gohr */ 353b69d74f1SAndreas Gohr public function getDependants() 354b69d74f1SAndreas Gohr { 355b69d74f1SAndreas Gohr $local = new Local(); 356b69d74f1SAndreas Gohr $extensions = $local->getExtensions(); 357b69d74f1SAndreas Gohr $dependants = []; 358b69d74f1SAndreas Gohr foreach ($extensions as $extension) { 359b69d74f1SAndreas Gohr if ( 360b69d74f1SAndreas Gohr in_array($this->getId(), $extension->getDependencyList()) && 361b69d74f1SAndreas Gohr $extension->isEnabled() 362b69d74f1SAndreas Gohr ) { 363b69d74f1SAndreas Gohr $dependants[$extension->getId()] = $extension; 364b69d74f1SAndreas Gohr } 365b69d74f1SAndreas Gohr } 366b69d74f1SAndreas Gohr return $dependants; 367b69d74f1SAndreas Gohr } 368b69d74f1SAndreas Gohr 369b69d74f1SAndreas Gohr /** 370b2a05b76SAndreas Gohr * Return the minimum PHP version required by the extension 371b2a05b76SAndreas Gohr * 372b2a05b76SAndreas Gohr * Empty if not set 373b2a05b76SAndreas Gohr * 374b2a05b76SAndreas Gohr * @return string 375b2a05b76SAndreas Gohr */ 376b2a05b76SAndreas Gohr public function getMinimumPHPVersion() 377b2a05b76SAndreas Gohr { 378b2a05b76SAndreas Gohr return $this->getTag('phpmin', ''); 379b2a05b76SAndreas Gohr } 380b2a05b76SAndreas Gohr 381b2a05b76SAndreas Gohr /** 382b2a05b76SAndreas Gohr * Return the minimum PHP version supported by the extension 383b2a05b76SAndreas Gohr * 384b2a05b76SAndreas Gohr * @return string 385b2a05b76SAndreas Gohr */ 386b2a05b76SAndreas Gohr public function getMaximumPHPVersion() 387b2a05b76SAndreas Gohr { 388b2a05b76SAndreas Gohr return $this->getTag('phpmax', ''); 389b2a05b76SAndreas Gohr } 390b2a05b76SAndreas Gohr 391b2a05b76SAndreas Gohr /** 392cf2dcf1bSAndreas Gohr * Is this extension a template? 393cf2dcf1bSAndreas Gohr * 394cf2dcf1bSAndreas Gohr * @return bool false if it is a plugin 395cf2dcf1bSAndreas Gohr */ 396cf2dcf1bSAndreas Gohr public function isTemplate() 397cf2dcf1bSAndreas Gohr { 398cf2dcf1bSAndreas Gohr return $this->type === self::TYPE_TEMPLATE; 399cf2dcf1bSAndreas Gohr } 400cf2dcf1bSAndreas Gohr 401cf2dcf1bSAndreas Gohr /** 402cf2dcf1bSAndreas Gohr * Is the extension installed locally? 403cf2dcf1bSAndreas Gohr * 404cf2dcf1bSAndreas Gohr * @return bool 405cf2dcf1bSAndreas Gohr */ 406cf2dcf1bSAndreas Gohr public function isInstalled() 407cf2dcf1bSAndreas Gohr { 408cf2dcf1bSAndreas Gohr return is_dir($this->getInstallDir()); 409cf2dcf1bSAndreas Gohr } 410cf2dcf1bSAndreas Gohr 411cf2dcf1bSAndreas Gohr /** 412cf2dcf1bSAndreas Gohr * Is the extension under git control? 413cf2dcf1bSAndreas Gohr * 414cf2dcf1bSAndreas Gohr * @return bool 415cf2dcf1bSAndreas Gohr */ 416cf2dcf1bSAndreas Gohr public function isGitControlled() 417cf2dcf1bSAndreas Gohr { 418cf2dcf1bSAndreas Gohr if (!$this->isInstalled()) return false; 419cf2dcf1bSAndreas Gohr return file_exists($this->getInstallDir() . '/.git'); 420cf2dcf1bSAndreas Gohr } 421cf2dcf1bSAndreas Gohr 422cf2dcf1bSAndreas Gohr /** 423cf2dcf1bSAndreas Gohr * If the extension is bundled 424cf2dcf1bSAndreas Gohr * 425cf2dcf1bSAndreas Gohr * @return bool If the extension is bundled 426cf2dcf1bSAndreas Gohr */ 427cf2dcf1bSAndreas Gohr public function isBundled() 428cf2dcf1bSAndreas Gohr { 429cf2dcf1bSAndreas Gohr $this->loadRemoteInfo(); 430cf2dcf1bSAndreas Gohr return $this->remoteInfo['bundled'] ?? in_array( 431cf2dcf1bSAndreas Gohr $this->getId(), 432cf2dcf1bSAndreas Gohr [ 433cf2dcf1bSAndreas Gohr 'authad', 434cf2dcf1bSAndreas Gohr 'authldap', 435cf2dcf1bSAndreas Gohr 'authpdo', 436cf2dcf1bSAndreas Gohr 'authplain', 437cf2dcf1bSAndreas Gohr 'acl', 438cf2dcf1bSAndreas Gohr 'config', 439cf2dcf1bSAndreas Gohr 'extension', 440cf2dcf1bSAndreas Gohr 'info', 441cf2dcf1bSAndreas Gohr 'popularity', 442cf2dcf1bSAndreas Gohr 'revert', 443cf2dcf1bSAndreas Gohr 'safefnrecode', 444cf2dcf1bSAndreas Gohr 'styling', 445cf2dcf1bSAndreas Gohr 'testing', 446cf2dcf1bSAndreas Gohr 'usermanager', 447cf2dcf1bSAndreas Gohr 'logviewer', 448cf2dcf1bSAndreas Gohr 'template:dokuwiki' 449cf2dcf1bSAndreas Gohr ] 450cf2dcf1bSAndreas Gohr ); 451cf2dcf1bSAndreas Gohr } 452cf2dcf1bSAndreas Gohr 453cf2dcf1bSAndreas Gohr /** 454cf2dcf1bSAndreas Gohr * Is the extension protected against any modification (disable/uninstall) 455cf2dcf1bSAndreas Gohr * 456cf2dcf1bSAndreas Gohr * @return bool if the extension is protected 457cf2dcf1bSAndreas Gohr */ 458cf2dcf1bSAndreas Gohr public function isProtected() 459cf2dcf1bSAndreas Gohr { 460cf2dcf1bSAndreas Gohr // never allow deinstalling the current auth plugin: 461cf2dcf1bSAndreas Gohr global $conf; 462cf2dcf1bSAndreas Gohr if ($this->getId() == $conf['authtype']) return true; 463cf2dcf1bSAndreas Gohr 464077f55feSAndreas Gohr // disallow current template to be uninstalled 465077f55feSAndreas Gohr if ($this->isTemplate() && ($this->getBase() === $conf['template'])) return true; 466cf2dcf1bSAndreas Gohr 467cf2dcf1bSAndreas Gohr /** @var PluginController $plugin_controller */ 468cf2dcf1bSAndreas Gohr global $plugin_controller; 469cf2dcf1bSAndreas Gohr $cascade = $plugin_controller->getCascade(); 470cf2dcf1bSAndreas Gohr return ($cascade['protected'][$this->getId()] ?? false); 471cf2dcf1bSAndreas Gohr } 472cf2dcf1bSAndreas Gohr 473cf2dcf1bSAndreas Gohr /** 474cf2dcf1bSAndreas Gohr * Is the extension installed in the correct directory? 475cf2dcf1bSAndreas Gohr * 476cf2dcf1bSAndreas Gohr * @return bool 477cf2dcf1bSAndreas Gohr */ 478cf2dcf1bSAndreas Gohr public function isInWrongFolder() 479cf2dcf1bSAndreas Gohr { 4804fd6a1d7SAndreas Gohr if (!$this->isInstalled()) return false; 481cf2dcf1bSAndreas Gohr return $this->getInstallDir() != $this->currentDir; 482cf2dcf1bSAndreas Gohr } 483cf2dcf1bSAndreas Gohr 484cf2dcf1bSAndreas Gohr /** 485cf2dcf1bSAndreas Gohr * Is the extension enabled? 486cf2dcf1bSAndreas Gohr * 487cf2dcf1bSAndreas Gohr * @return bool 488cf2dcf1bSAndreas Gohr */ 489cf2dcf1bSAndreas Gohr public function isEnabled() 490cf2dcf1bSAndreas Gohr { 491cf2dcf1bSAndreas Gohr global $conf; 492cf2dcf1bSAndreas Gohr if ($this->isTemplate()) { 493cf2dcf1bSAndreas Gohr return ($conf['template'] == $this->getBase()); 494cf2dcf1bSAndreas Gohr } 495cf2dcf1bSAndreas Gohr 496cf2dcf1bSAndreas Gohr /* @var PluginController $plugin_controller */ 497cf2dcf1bSAndreas Gohr global $plugin_controller; 498cf2dcf1bSAndreas Gohr return $plugin_controller->isEnabled($this->base); 499cf2dcf1bSAndreas Gohr } 500cf2dcf1bSAndreas Gohr 501160d3688SAndreas Gohr /** 502160d3688SAndreas Gohr * Has the download URL changed since the last download? 503160d3688SAndreas Gohr * 504160d3688SAndreas Gohr * @return bool 505160d3688SAndreas Gohr */ 506160d3688SAndreas Gohr public function hasChangedURL() 507160d3688SAndreas Gohr { 5084fd6a1d7SAndreas Gohr $last = $this->getManager()->getDownloadURL(); 509160d3688SAndreas Gohr if (!$last) return false; 510*2ee9c305Sfiwswe $url = $this->getDownloadURL(); 511*2ee9c305Sfiwswe if (!$url) return false; 512*2ee9c305Sfiwswe return $last !== $url; 513160d3688SAndreas Gohr } 514160d3688SAndreas Gohr 515160d3688SAndreas Gohr /** 516160d3688SAndreas Gohr * Is an update available for this extension? 517160d3688SAndreas Gohr * 518160d3688SAndreas Gohr * @return bool 519160d3688SAndreas Gohr */ 5204fd6a1d7SAndreas Gohr public function isUpdateAvailable() 521160d3688SAndreas Gohr { 522160d3688SAndreas Gohr if ($this->isBundled()) return false; // bundled extensions are never updated 523160d3688SAndreas Gohr $self = $this->getInstalledVersion(); 524160d3688SAndreas Gohr $remote = $this->getLastUpdate(); 525160d3688SAndreas Gohr return $self < $remote; 526160d3688SAndreas Gohr } 527160d3688SAndreas Gohr 528cf2dcf1bSAndreas Gohr // endregion 529cf2dcf1bSAndreas Gohr 5307c9966a5SAndreas Gohr // region Remote Info 5317c9966a5SAndreas Gohr 5327c9966a5SAndreas Gohr /** 5337c9966a5SAndreas Gohr * Get the date of the last available update 5347c9966a5SAndreas Gohr * 5357c9966a5SAndreas Gohr * @return string yyyy-mm-dd 5367c9966a5SAndreas Gohr */ 5377c9966a5SAndreas Gohr public function getLastUpdate() 5387c9966a5SAndreas Gohr { 5397c9966a5SAndreas Gohr return $this->getRemoteTag('lastupdate'); 5407c9966a5SAndreas Gohr } 5417c9966a5SAndreas Gohr 5427c9966a5SAndreas Gohr /** 5437c9966a5SAndreas Gohr * Get a list of tags this extension is tagged with at dokuwiki.org 5447c9966a5SAndreas Gohr * 5457c9966a5SAndreas Gohr * @return string[] 5467c9966a5SAndreas Gohr */ 5477c9966a5SAndreas Gohr public function getTags() 5487c9966a5SAndreas Gohr { 5497c9966a5SAndreas Gohr return $this->getRemoteTag('tags', []); 5507c9966a5SAndreas Gohr } 5517c9966a5SAndreas Gohr 5527c9966a5SAndreas Gohr /** 5537c9966a5SAndreas Gohr * Get the popularity of the extension 5547c9966a5SAndreas Gohr * 5557c9966a5SAndreas Gohr * This is a float between 0 and 1 5567c9966a5SAndreas Gohr * 5577c9966a5SAndreas Gohr * @return float 5587c9966a5SAndreas Gohr */ 5597c9966a5SAndreas Gohr public function getPopularity() 5607c9966a5SAndreas Gohr { 5617c9966a5SAndreas Gohr return (float)$this->getRemoteTag('popularity', 0); 5627c9966a5SAndreas Gohr } 5637c9966a5SAndreas Gohr 5647c9966a5SAndreas Gohr /** 5657c9966a5SAndreas Gohr * Get the text of the update message if there is any 5667c9966a5SAndreas Gohr * 5677c9966a5SAndreas Gohr * @return string 5687c9966a5SAndreas Gohr */ 5697c9966a5SAndreas Gohr public function getUpdateMessage() 5707c9966a5SAndreas Gohr { 5717c9966a5SAndreas Gohr return $this->getRemoteTag('updatemessage'); 5727c9966a5SAndreas Gohr } 5737c9966a5SAndreas Gohr 5747c9966a5SAndreas Gohr /** 5757c9966a5SAndreas Gohr * Get the text of the security warning if there is any 5767c9966a5SAndreas Gohr * 5777c9966a5SAndreas Gohr * @return string 5787c9966a5SAndreas Gohr */ 5797c9966a5SAndreas Gohr public function getSecurityWarning() 5807c9966a5SAndreas Gohr { 5817c9966a5SAndreas Gohr return $this->getRemoteTag('securitywarning'); 5827c9966a5SAndreas Gohr } 5837c9966a5SAndreas Gohr 5847c9966a5SAndreas Gohr /** 5857c9966a5SAndreas Gohr * Get the text of the security issue if there is any 5867c9966a5SAndreas Gohr * 5877c9966a5SAndreas Gohr * @return string 5887c9966a5SAndreas Gohr */ 5897c9966a5SAndreas Gohr public function getSecurityIssue() 5907c9966a5SAndreas Gohr { 5917c9966a5SAndreas Gohr return $this->getRemoteTag('securityissue'); 5927c9966a5SAndreas Gohr } 5937c9966a5SAndreas Gohr 5947c9966a5SAndreas Gohr /** 5957c9966a5SAndreas Gohr * Get the URL of the screenshot of the extension if there is any 5967c9966a5SAndreas Gohr * 5977c9966a5SAndreas Gohr * @return string 5987c9966a5SAndreas Gohr */ 5997c9966a5SAndreas Gohr public function getScreenshotURL() 6007c9966a5SAndreas Gohr { 6017c9966a5SAndreas Gohr return $this->getRemoteTag('screenshoturl'); 6027c9966a5SAndreas Gohr } 6037c9966a5SAndreas Gohr 6047c9966a5SAndreas Gohr /** 6057c9966a5SAndreas Gohr * Get the URL of the thumbnail of the extension if there is any 6067c9966a5SAndreas Gohr * 6077c9966a5SAndreas Gohr * @return string 6087c9966a5SAndreas Gohr */ 6097c9966a5SAndreas Gohr public function getThumbnailURL() 6107c9966a5SAndreas Gohr { 6117c9966a5SAndreas Gohr return $this->getRemoteTag('thumbnailurl'); 6127c9966a5SAndreas Gohr } 6137c9966a5SAndreas Gohr 6147c9966a5SAndreas Gohr /** 6157c9966a5SAndreas Gohr * Get the download URL of the extension if there is any 6167c9966a5SAndreas Gohr * 6177c9966a5SAndreas Gohr * @return string 6187c9966a5SAndreas Gohr */ 6197c9966a5SAndreas Gohr public function getDownloadURL() 6207c9966a5SAndreas Gohr { 6217c9966a5SAndreas Gohr return $this->getRemoteTag('downloadurl'); 6227c9966a5SAndreas Gohr } 6237c9966a5SAndreas Gohr 6247c9966a5SAndreas Gohr /** 6257c9966a5SAndreas Gohr * Get the bug tracker URL of the extension if there is any 6267c9966a5SAndreas Gohr * 6277c9966a5SAndreas Gohr * @return string 6287c9966a5SAndreas Gohr */ 6297c9966a5SAndreas Gohr public function getBugtrackerURL() 6307c9966a5SAndreas Gohr { 6317c9966a5SAndreas Gohr return $this->getRemoteTag('bugtracker'); 6327c9966a5SAndreas Gohr } 6337c9966a5SAndreas Gohr 6347c9966a5SAndreas Gohr /** 6357c9966a5SAndreas Gohr * Get the URL of the source repository if there is any 6367c9966a5SAndreas Gohr * 6377c9966a5SAndreas Gohr * @return string 6387c9966a5SAndreas Gohr */ 6397c9966a5SAndreas Gohr public function getSourcerepoURL() 6407c9966a5SAndreas Gohr { 6417c9966a5SAndreas Gohr return $this->getRemoteTag('sourcerepo'); 6427c9966a5SAndreas Gohr } 6437c9966a5SAndreas Gohr 6447c9966a5SAndreas Gohr /** 6457c9966a5SAndreas Gohr * Get the donation URL of the extension if there is any 6467c9966a5SAndreas Gohr * 6477c9966a5SAndreas Gohr * @return string 6487c9966a5SAndreas Gohr */ 6497c9966a5SAndreas Gohr public function getDonationURL() 6507c9966a5SAndreas Gohr { 6517c9966a5SAndreas Gohr return $this->getRemoteTag('donationurl'); 6527c9966a5SAndreas Gohr } 6537c9966a5SAndreas Gohr 6544fd6a1d7SAndreas Gohr /** 6554fd6a1d7SAndreas Gohr * Get a list of extensions that are similar to this one 6564fd6a1d7SAndreas Gohr * 6574fd6a1d7SAndreas Gohr * @return string[] 6584fd6a1d7SAndreas Gohr */ 6594fd6a1d7SAndreas Gohr public function getSimilarList() 6604fd6a1d7SAndreas Gohr { 6614fd6a1d7SAndreas Gohr return $this->getRemoteTag('similar', []); 6624fd6a1d7SAndreas Gohr } 6634fd6a1d7SAndreas Gohr 6644fd6a1d7SAndreas Gohr /** 6654fd6a1d7SAndreas Gohr * Get a list of extensions that are marked as conflicting with this one 6664fd6a1d7SAndreas Gohr * 6674fd6a1d7SAndreas Gohr * @return string[] 6684fd6a1d7SAndreas Gohr */ 6694fd6a1d7SAndreas Gohr public function getConflictList() 6704fd6a1d7SAndreas Gohr { 6714fd6a1d7SAndreas Gohr return $this->getRemoteTag('conflicts', []); 6724fd6a1d7SAndreas Gohr } 6734fd6a1d7SAndreas Gohr 6744fd6a1d7SAndreas Gohr /** 6754fd6a1d7SAndreas Gohr * Get a list of DokuWiki versions this plugin is marked as compatible with 6764fd6a1d7SAndreas Gohr * 6774fd6a1d7SAndreas Gohr * @return string[][] date -> version 6784fd6a1d7SAndreas Gohr */ 6794fd6a1d7SAndreas Gohr public function getCompatibleVersions() 6804fd6a1d7SAndreas Gohr { 6814fd6a1d7SAndreas Gohr return $this->getRemoteTag('compatible', []); 6824fd6a1d7SAndreas Gohr } 6834fd6a1d7SAndreas Gohr 6847c9966a5SAndreas Gohr // endregion 6857c9966a5SAndreas Gohr 686cf2dcf1bSAndreas Gohr // region Actions 687cf2dcf1bSAndreas Gohr 688cf2dcf1bSAndreas Gohr /** 689cf2dcf1bSAndreas Gohr * Install or update the extension 690cf2dcf1bSAndreas Gohr * 691cf2dcf1bSAndreas Gohr * @throws Exception 692cf2dcf1bSAndreas Gohr */ 693cf2dcf1bSAndreas Gohr public function installOrUpdate() 694cf2dcf1bSAndreas Gohr { 695cf2dcf1bSAndreas Gohr $installer = new Installer(true); 696160d3688SAndreas Gohr $installer->installExtension($this); 697cf2dcf1bSAndreas Gohr } 698cf2dcf1bSAndreas Gohr 699cf2dcf1bSAndreas Gohr /** 700cf2dcf1bSAndreas Gohr * Uninstall the extension 701cf2dcf1bSAndreas Gohr * @throws Exception 702cf2dcf1bSAndreas Gohr */ 703cf2dcf1bSAndreas Gohr public function uninstall() 704cf2dcf1bSAndreas Gohr { 705cf2dcf1bSAndreas Gohr $installer = new Installer(true); 706cf2dcf1bSAndreas Gohr $installer->uninstall($this); 707cf2dcf1bSAndreas Gohr } 708cf2dcf1bSAndreas Gohr 709cf2dcf1bSAndreas Gohr /** 7105732c960SAndreas Gohr * Toggle the extension between enabled and disabled 7115732c960SAndreas Gohr * @return void 7125732c960SAndreas Gohr * @throws Exception 7135732c960SAndreas Gohr */ 7145732c960SAndreas Gohr public function toggle() 7155732c960SAndreas Gohr { 7165732c960SAndreas Gohr if ($this->isEnabled()) { 7175732c960SAndreas Gohr $this->disable(); 7185732c960SAndreas Gohr } else { 7195732c960SAndreas Gohr $this->enable(); 7205732c960SAndreas Gohr } 7215732c960SAndreas Gohr } 7225732c960SAndreas Gohr 7235732c960SAndreas Gohr /** 724cf2dcf1bSAndreas Gohr * Enable the extension 725b69d74f1SAndreas Gohr * 726cf2dcf1bSAndreas Gohr * @throws Exception 727cf2dcf1bSAndreas Gohr */ 728cf2dcf1bSAndreas Gohr public function enable() 729cf2dcf1bSAndreas Gohr { 730b69d74f1SAndreas Gohr (new Installer())->enable($this); 731cf2dcf1bSAndreas Gohr } 732cf2dcf1bSAndreas Gohr 733cf2dcf1bSAndreas Gohr /** 734cf2dcf1bSAndreas Gohr * Disable the extension 735b69d74f1SAndreas Gohr * 736cf2dcf1bSAndreas Gohr * @throws Exception 737cf2dcf1bSAndreas Gohr */ 738cf2dcf1bSAndreas Gohr public function disable() 739cf2dcf1bSAndreas Gohr { 740b69d74f1SAndreas Gohr (new Installer())->disable($this); 741cf2dcf1bSAndreas Gohr } 742cf2dcf1bSAndreas Gohr 743cf2dcf1bSAndreas Gohr // endregion 744cf2dcf1bSAndreas Gohr 745cf2dcf1bSAndreas Gohr // region Meta Data Management 746cf2dcf1bSAndreas Gohr 747cf2dcf1bSAndreas Gohr /** 7487c9966a5SAndreas Gohr * Access the Manager for this extension 749cf2dcf1bSAndreas Gohr * 7507c9966a5SAndreas Gohr * @return Manager 751cf2dcf1bSAndreas Gohr */ 7527c9966a5SAndreas Gohr public function getManager() 753cf2dcf1bSAndreas Gohr { 7547c184cfcSAndreas Gohr if (!$this->manager instanceof Manager) { 7557c9966a5SAndreas Gohr $this->manager = new Manager($this); 756cf2dcf1bSAndreas Gohr } 7577c9966a5SAndreas Gohr return $this->manager; 758cf2dcf1bSAndreas Gohr } 759cf2dcf1bSAndreas Gohr 760cf2dcf1bSAndreas Gohr /** 761cf2dcf1bSAndreas Gohr * Reads the info file of the extension if available and fills the localInfo array 762cf2dcf1bSAndreas Gohr */ 763cf2dcf1bSAndreas Gohr protected function readLocalInfo() 764cf2dcf1bSAndreas Gohr { 765a1e045f7SAndreas Gohr if (!$this->getCurrentDir()) return; 766cf2dcf1bSAndreas Gohr $file = $this->currentDir . '/' . $this->type . '.info.txt'; 767cf2dcf1bSAndreas Gohr if (!is_readable($file)) return; 768cf2dcf1bSAndreas Gohr $this->localInfo = confToHash($file, true); 769cf2dcf1bSAndreas Gohr $this->localInfo = array_filter($this->localInfo); // remove all falsy keys 770cf2dcf1bSAndreas Gohr } 771cf2dcf1bSAndreas Gohr 772cf2dcf1bSAndreas Gohr /** 773cf2dcf1bSAndreas Gohr * Fetches the remote info from the repository 774cf2dcf1bSAndreas Gohr * 775cf2dcf1bSAndreas Gohr * This ignores any errors coming from the repository and just sets the remoteInfo to an empty array in that case 776cf2dcf1bSAndreas Gohr */ 777cf2dcf1bSAndreas Gohr protected function loadRemoteInfo() 778cf2dcf1bSAndreas Gohr { 779cf2dcf1bSAndreas Gohr if ($this->remoteInfo) return; 780cf2dcf1bSAndreas Gohr $remote = Repository::getInstance(); 781cf2dcf1bSAndreas Gohr try { 782cf2dcf1bSAndreas Gohr $this->remoteInfo = (array)$remote->getExtensionData($this->getId()); 783cf2dcf1bSAndreas Gohr } catch (Exception $e) { 784cf2dcf1bSAndreas Gohr $this->remoteInfo = []; 785cf2dcf1bSAndreas Gohr } 786cf2dcf1bSAndreas Gohr } 787cf2dcf1bSAndreas Gohr 788cf2dcf1bSAndreas Gohr /** 789cf2dcf1bSAndreas Gohr * Read information from either local or remote info 790cf2dcf1bSAndreas Gohr * 7917c9966a5SAndreas Gohr * Always prefers local info over remote info. Giving multiple keys is useful when the 7927c9966a5SAndreas Gohr * key has been renamed in the past or if local and remote keys might differ. 793cf2dcf1bSAndreas Gohr * 794cf2dcf1bSAndreas Gohr * @param string|string[] $tag one or multiple keys to check 795cf2dcf1bSAndreas Gohr * @param mixed $default 796cf2dcf1bSAndreas Gohr * @return mixed 797cf2dcf1bSAndreas Gohr */ 798cf2dcf1bSAndreas Gohr protected function getTag($tag, $default = '') 799cf2dcf1bSAndreas Gohr { 800cf2dcf1bSAndreas Gohr foreach ((array)$tag as $t) { 801cf2dcf1bSAndreas Gohr if (isset($this->localInfo[$t])) return $this->localInfo[$t]; 802cf2dcf1bSAndreas Gohr } 8037c9966a5SAndreas Gohr 8047c9966a5SAndreas Gohr return $this->getRemoteTag($tag, $default); 8057c9966a5SAndreas Gohr } 8067c9966a5SAndreas Gohr 8077c9966a5SAndreas Gohr /** 8087c9966a5SAndreas Gohr * Read information from remote info 8097c9966a5SAndreas Gohr * 8107c9966a5SAndreas Gohr * @param string|string[] $tag one or mutiple keys to check 8117c9966a5SAndreas Gohr * @param mixed $default 8127c9966a5SAndreas Gohr * @return mixed 8137c9966a5SAndreas Gohr */ 8147c9966a5SAndreas Gohr protected function getRemoteTag($tag, $default = '') 8157c9966a5SAndreas Gohr { 816cf2dcf1bSAndreas Gohr $this->loadRemoteInfo(); 817cf2dcf1bSAndreas Gohr foreach ((array)$tag as $t) { 818cf2dcf1bSAndreas Gohr if (isset($this->remoteInfo[$t])) return $this->remoteInfo[$t]; 819cf2dcf1bSAndreas Gohr } 820cf2dcf1bSAndreas Gohr return $default; 821cf2dcf1bSAndreas Gohr } 822cf2dcf1bSAndreas Gohr 823cf2dcf1bSAndreas Gohr // endregion 824a1e045f7SAndreas Gohr 825a1e045f7SAndreas Gohr // region utilities 826a1e045f7SAndreas Gohr 827a1e045f7SAndreas Gohr /** 828a1e045f7SAndreas Gohr * Convert an extension id to a type and base 829a1e045f7SAndreas Gohr * 830a1e045f7SAndreas Gohr * @param string $id 831a1e045f7SAndreas Gohr * @return array [type, base] 832a1e045f7SAndreas Gohr */ 833a1e045f7SAndreas Gohr protected function idToTypeBase($id) 834a1e045f7SAndreas Gohr { 835a1e045f7SAndreas Gohr [$type, $base] = sexplode(':', $id, 2); 836a1e045f7SAndreas Gohr if ($base === null) { 837a1e045f7SAndreas Gohr $base = $type; 838a1e045f7SAndreas Gohr $type = self::TYPE_PLUGIN; 839a1e045f7SAndreas Gohr } elseif ($type === self::TYPE_TEMPLATE) { 840a1e045f7SAndreas Gohr $type = self::TYPE_TEMPLATE; 841a1e045f7SAndreas Gohr } else { 842a1e045f7SAndreas Gohr throw new RuntimeException('Invalid extension id: ' . $id); 843a1e045f7SAndreas Gohr } 844a1e045f7SAndreas Gohr 845a1e045f7SAndreas Gohr return [$type, $base]; 846a1e045f7SAndreas Gohr } 847b69d74f1SAndreas Gohr 8487c9966a5SAndreas Gohr /** 8497c9966a5SAndreas Gohr * @return string 8507c9966a5SAndreas Gohr */ 8517c9966a5SAndreas Gohr public function __toString() 8527c9966a5SAndreas Gohr { 8537c9966a5SAndreas Gohr return $this->getId(); 8547c9966a5SAndreas Gohr } 8557c9966a5SAndreas Gohr 856a1e045f7SAndreas Gohr // endregion 857cf2dcf1bSAndreas Gohr} 858