Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/AddOn/Manager.php
<?php

namespace XF\AddOn;

use function
in_array, is_array;

class
Manager
{
    protected
$addOnDir;

    protected
$jsonInfo;

   
/**
     * @var \XF\Entity\AddOn[]|null
     */
   
protected $installedAddOns;

   
/**
     * @var AddOn[]|null
     */
   
protected $allAddOns;

    public function
__construct($addOnDir)
    {
       
$this->addOnDir = rtrim($addOnDir, '/\\');
    }

   
/**
     * @return AddOn[]
     */
   
public function getAllAddOns()
    {
        if (!
is_array($this->allAddOns))
        {
           
$installed = $this->getInstalledEntities();
           
$handlers = [];

            foreach (
$this->getAllJsonInfo() AS $id => $info)
            {
               
$existing = $installed[$id] ?? null;
               
$handlers[$id] = $this->loadAddOnClass($existing, $info);
            }

            foreach (
$installed AS $id => $existing)
            {
                if (!isset(
$handlers[$id]))
                {
                   
$handlers[$id] = $this->loadAddOnClass($existing);
                }
            }

           
$handlers = \XF\Util\Arr::columnSort($handlers, 'title', 'strcasecmp');

           
$this->allAddOns = $handlers;
        }

        return
$this->allAddOns;
    }

   
/**
     * @return AddOn[]
     */
   
public function getInstalledAddOns()
    {
       
$addOns = $this->getAllAddOns();
        foreach (
$addOns AS $k => $addOn)
        {
            if (!
$addOn->isInstalled())
            {
                unset(
$addOns[$k]);
            }
        }

        return
$addOns;
    }

    public function
getAddOnHashes($exclude = null): array
    {
        if (
$exclude === null)
        {
           
$exclude = [];
        }
        if (!
is_array($exclude))
        {
           
$exclude = [$exclude];
        }

       
$hashes = [];

        foreach (
$this->getAllAddOns() AS $addOn)
        {
            if (
in_array($addOn->getAddOnId(), $exclude))
            {
                continue;
            }

           
$addOnHashes = $addOn->getHashes();
            if (
$addOnHashes)
            {
               
$hashes = array_replace($hashes, $addOnHashes);
            }
        }

       
ksort($hashes, SORT_NATURAL | SORT_FLAG_CASE);

        return
$hashes;
    }

    public function
getById($addOnId)
    {
       
$addOnId = $this->coerceAddOnId($addOnId);

       
$installedList = $this->getInstalledEntities();
       
$installed = ($installedList[$addOnId] ?? null);
       
$jsonInfo = $this->getAddOnJsonInfo($addOnId);

        if (!
$installed && !$jsonInfo)
        {
            return
null;
        }

        return
$this->loadAddOnClass($installed, $jsonInfo);
    }

    protected function
coerceAddOnId($addOnId)
    {
       
$addOnIds = $this->getAvailableAddOnIds();

       
$index = array_search(strtoupper($addOnId), array_map('strtoupper', $addOnIds));

        if (
$index !== false)
        {
            return
$addOnIds[$index];
        }

        return
$addOnId;
    }

   
/**
     * @return \XF\Entity\AddOn[]
     */
   
protected function getInstalledEntities()
    {
        if (!
is_array($this->installedAddOns))
        {
           
$this->installedAddOns = \XF::em()->getFinder('XF:AddOn')->fetch()->toArray();
            unset(
$this->installedAddOns['XF']);
        }

        return
$this->installedAddOns;
    }

    public function
resetAddOnCache()
    {
       
$this->installedAddOns = null;
       
$this->allAddOns = null;
    }

    protected function
isValidDir(\DirectoryIterator $entry)
    {
       
/** @var \DirectoryIterator $entry */
       
if (!$entry->isDir()
            ||
$entry->isDot()
            || !
preg_match('/^[a-z0-9_]+$/i', $entry->getBasename())
        )
        {
            return
false;
        }

        return
true;
    }

    protected function
prepareAddOnIdForPath($addOnId)
    {
        if (
strpos($addOnId, '/') !== false)
        {
           
$addOnId = str_replace('/', \XF::$DS, $addOnId);
        }
        return
$addOnId;
    }

    public function
isDirAddOnRoot(\DirectoryIterator $entry)
    {
       
$ds = \XF::$DS;

       
$pathname = $entry->getPathname();
       
$addOnJson = "{$pathname}{$ds}addon.json";
       
$outputDir = "{$pathname}{$ds}_output";
       
$dataDir = "{$pathname}{$ds}_data";

        if (
file_exists($addOnJson) || file_exists($outputDir) || file_exists($dataDir))
        {
            return
true;
        }
        else
        {
            return
false;
        }
    }

    public function
getAvailableAddOnIds()
    {
       
$addOnIds = [];
        foreach (new \
DirectoryIterator($this->addOnDir) AS $entry)
        {
            if (!
$this->isValidDir($entry))
            {
                continue;
            }

            if (
$this->isDirAddOnRoot($entry) || $entry->getBasename() == 'XF')
            {
               
$addOnIds[] = $entry->getBasename();
            }
            else
            {
               
$vendorPrefix = $entry->getBasename();
                foreach (new \
DirectoryIterator($entry->getPathname()) AS $addOnDir)
                {
                    if (!
$this->isValidDir($addOnDir))
                    {
                        continue;
                    }

                    if (
$this->isDirAddOnRoot($addOnDir))
                    {
                       
$addOnIds[] = "$vendorPrefix/{$addOnDir->getBasename()}";
                    }
                }
            }
        }
        return
$addOnIds;
    }

    protected function
getAllJsonInfo()
    {
        if (!
is_array($this->jsonInfo))
        {
           
$available = [];

           
$addOnIds = $this->getAvailableAddOnIds();
            foreach (
$addOnIds AS $addOnId)
            {
               
$addOnIdDir = $this->prepareAddOnIdForPath($addOnId);
                foreach (new \
DirectoryIterator($this->addOnDir . \XF::$DS . $addOnIdDir) AS $file)
                {
                   
$hasJson = ($file->isFile() && $file->getBasename() === 'addon.json');
                    if (
$hasJson)
                    {
                       
$available[$addOnId] = $this->getAddOnJsonInfo($addOnId);
                        break;
                    }
                }
            }

           
$this->jsonInfo = $available;
        }

        return
$this->jsonInfo;
    }

    protected function
getAddOnJsonInfo($addOnId)
    {
        if (!
preg_match('#^[a-z][a-z0-9]*(/[a-z][a-z0-9]*)?$#i', $addOnId))
        {
            return
null;
        }

       
$file = $this->getAddOnJsonFile($addOnId);
        if (!
file_exists($file) || !is_readable($file))
        {
            return
null;
        }

       
$json = json_decode(file_get_contents($file), true);
       
$json['addon_id'] = $addOnId;
        return
$json;
    }

    public function
getAddOnPath($addOnId)
    {
       
$addOnIdDir = $this->prepareAddOnIdForPath($addOnId);
        return
$this->addOnDir . \XF::$DS . $addOnIdDir;
    }

    public function
getAddOnJsonFile($addOnId)
    {
        return
$this->getAddOnPath($addOnId) . \XF::$DS . 'addon.json';
    }

    public function
checkAddOnRequirements(array $requirements, $title, &$errors = [])
    {
       
$errors = [];
       
$addOns = \XF::app()->container('addon.cache');

        foreach (
$requirements AS $productKey => $requirement)
        {
            if (!
is_array($requirement))
            {
                continue;
            }
            list (
$version, $product) = $requirement;

           
$enabled = false;
           
$versionValid = false;

            if (
strpos($productKey, 'php-ext/') === 0)
            {
               
$parts = explode('/', $productKey, 2);
                if (isset(
$parts[1]))
                {
                   
$enabled = extension_loaded($parts[1]);

                    if (
$version === '*')
                    {
                       
$versionValid = true;
                    }
                    else
                    {
                       
$versionValid = version_compare(phpversion($parts[1]), $version, '>=');
                    }
                }
            }
            else if (
$productKey === 'php')
            {
               
$enabled = true;
               
$versionValid = version_compare(phpversion(), $version, '>=');
            }
            else if (
$productKey === 'mysql')
            {
               
$mySqlVersion = \XF::db()->getServerVersion();
                if (
$mySqlVersion)
                {
                   
$enabled = true;
                   
$versionValid = version_compare(strtolower($mySqlVersion), $version, '>=');
                }
            }
            else
            {
               
$enabled = isset($addOns[$productKey]);
               
$versionValid = ($version === '*' || ($enabled && $addOns[$productKey] >= $version));
            }

            if (!
$enabled || !$versionValid)
            {
               
$errors[] = "{$title} requires $product.";
            }
        }

        return
$errors ? false : true;
    }

   
/**
     * @param $addOnOrId
     *
     * @return AddOn
     */
   
protected function loadAddOnClass($addOnOrId, array $jsonInfo = null)
    {
        if (!
$addOnOrId)
        {
           
$addOnOrId = $jsonInfo['addon_id'] ?? null;
        }
        if (!
$addOnOrId)
        {
            throw new \
InvalidArgumentException("Must provide an existing add-on or add-on JSON");
        }

        return new
AddOn($addOnOrId, $this);
    }

    public function
canDeleteFilesForAddOn(string $addOnId): bool
   
{
        return
true;
    }
}