Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,6 @@ public function finaliseUpgrade()
$installer->extension = new \Joomla\CMS\Table\Extension($db);
$installer->extension->load(ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id);

$installer->setAdapter($installer->extension->type);

$installer->setPath('manifest', JPATH_MANIFESTS . '/files/joomla.xml');
$installer->setPath('source', JPATH_MANIFESTS . '/files');
$installer->setPath('extension_root', JPATH_ROOT);
Expand Down
6 changes: 5 additions & 1 deletion libraries/src/Console/ExtensionInstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

namespace Joomla\CMS\Console;

use Joomla\CMS\Factory;
use Joomla\CMS\Installer\Installer;
use Joomla\CMS\Installer\InstallerHelper;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Database\DatabaseInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
Expand Down Expand Up @@ -130,7 +132,8 @@ public function processPathInstallation($path): bool
return false;
}

$jInstaller = Installer::getInstance();
$jInstaller = new Installer();
$jInstaller->setDatabase(Factory::getContainer()->get(DatabaseInterface::class));
$result = $jInstaller->install($package['extractdir']);
InstallerHelper::cleanupInstall($tmpPath, $package['extractdir']);

Expand Down Expand Up @@ -163,6 +166,7 @@ public function processUrlInstallation($url): bool
}

$jInstaller = new Installer();
$jInstaller->setDatabase(Factory::getContainer()->get(DatabaseInterface::class));
$result = $jInstaller->install($package['extractdir']);
InstallerHelper::cleanupInstall($path, $package['extractdir']);

Expand Down
192 changes: 141 additions & 51 deletions libraries/src/Installer/Installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

namespace Joomla\CMS\Installer;

use Joomla\CMS\Adapter\Adapter;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Event\Extension\AfterInstallEvent;
use Joomla\CMS\Event\Extension\AfterUninstallEvent;
Expand All @@ -20,6 +19,8 @@
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Object\LegacyErrorHandlingTrait;
use Joomla\CMS\Object\LegacyPropertyManagementTrait;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Extension;
use Joomla\CMS\Table\Table;
Expand All @@ -42,9 +43,35 @@
*
* @since 3.1
*/
class Installer extends Adapter implements DatabaseAwareInterface
class Installer implements DatabaseAwareInterface
{
use DatabaseAwareTrait;
use LegacyErrorHandlingTrait;
use LegacyPropertyManagementTrait;

/**
* Array of installer adapters
*
* @var string[]|InstallerAdapter[]
* @since __DEPLOY_VERSION__
*/
private $adapters = [];

/**
* Adapter Class Prefix
*
* @var string
* @since __DEPLOY_VERSION__
*/
private $classprefix = '\\Joomla\\CMS\\Installer\\Adapter';

/**
* Base Path for the installer adapters
*
* @var string
* @since __DEPLOY_VERSION__
*/
private $adapterfolder;

/**
* Array of paths needed by the installer
Expand Down Expand Up @@ -176,7 +203,9 @@ class Installer extends Adapter implements DatabaseAwareInterface
*/
public function __construct($basepath = __DIR__, $classprefix = '\\Joomla\\CMS\\Installer\\Adapter', $adapterfolder = 'Adapter')
{
parent::__construct($basepath, $classprefix, $adapterfolder);
$this->adapterfolder = $basepath . '/' . $adapterfolder;
$this->classprefix = $classprefix;
$this->loadAdapters();

$this->extension = Table::getInstance('Extension');
}
Expand Down Expand Up @@ -574,13 +603,20 @@ public function abort($msg = null, $type = null)
break;

default:
if ($type && \is_object($this->_adapters[$type])) {
if ($type) {
try {
$adapter = $this->getAdapter($type);
} catch (\InvalidArgumentException $e) {
$stepval = false;
break;
}

// Build the name of the custom rollback method for the type
$method = '_rollback_' . $step['type'];

// Custom rollback method handler
if (method_exists($this->_adapters[$type], $method)) {
$stepval = $this->_adapters[$type]->$method($step);
if (method_exists($adapter, $method)) {
$stepval = $adapter->$method($step);
}
} else {
// Set it to false
Expand Down Expand Up @@ -703,7 +739,7 @@ public function discover_install($eid = null)
$type = $this->extension->type;
$params = ['extension' => $this->extension, 'route' => 'discover_install'];

$adapter = $this->loadAdapter($type, $params);
$adapter = $this->getAdapter($type, $params);

if (!\is_object($adapter)) {
return false;
Expand Down Expand Up @@ -775,7 +811,7 @@ public function discover()
$results = [];

foreach ($this->getAdapters() as $adapter) {
$instance = $this->loadAdapter($adapter);
$instance = $this->getAdapter($adapter);

// Joomla! 1.5 installation adapter legacy support
if (method_exists($instance, 'discover')) {
Expand Down Expand Up @@ -865,7 +901,7 @@ public function uninstall($type, $identifier)
{
$params = ['extension' => $this->extension, 'route' => 'uninstall'];

$adapter = $this->loadAdapter($type, $params);
$adapter = $this->getAdapter($type, $params);

if (!\is_object($adapter)) {
return false;
Expand Down Expand Up @@ -921,7 +957,7 @@ public function refreshManifestCache($eid)
}

// Fetch the adapter
$adapter = $this->loadAdapter($this->extension->type);
$adapter = $this->getAdapter($this->extension->type);

if (!\is_object($adapter)) {
return false;
Expand Down Expand Up @@ -972,7 +1008,7 @@ public function setupInstall($route = 'install', $returnAdapter = false)
$params = ['route' => $route, 'manifest' => $this->getManifest()];

// Load the adapter
$adapter = $this->loadAdapter($type, $params);
$adapter = $this->getAdapter($type, $params);

if ($returnAdapter) {
return $adapter;
Expand All @@ -994,7 +1030,7 @@ public function setupInstall($route = 'install', $returnAdapter = false)
public function parseQueries(\SimpleXMLElement $element)
{
// Get the database connector object
$db = & $this->_db;
$db = $this->getDatabase();

if (!$element || !\count($element->children())) {
// Either the tag does not exist or has no children therefore we return zero files processed.
Expand Down Expand Up @@ -1043,7 +1079,7 @@ public function parseSQLFiles($element)
return 0;
}

$db = &$this->_db;
$db = $this->getDatabase();
$dbDriver = $db->getServerType();
$updateCount = 0;

Expand Down Expand Up @@ -2349,110 +2385,164 @@ public static function parseXMLInstallFile($path)
}

/**
* Gets a list of available install adapters.
* Discover all adapters in the adapterfolder path
*
* @param array $options An array of options to inject into the adapter
* @param array $custom Array of custom install adapters
*
* @return string[] An array of the class names of available install adapters.
* @return void
*
* @since 3.4
* @since __DEPLOY_VERSION__
*/
public function getAdapters($options = [], array $custom = [])
protected function loadAdapters()
{
$files = new \DirectoryIterator($this->_basepath . '/' . $this->_adapterfolder);
$adapters = [];
$files = new \DirectoryIterator($this->adapterfolder);

// Process the core adapters
foreach ($files as $file) {
$fileName = $file->getFilename();

// Only load for php files.
// Only load php files.
if (!$file->isFile() || $file->getExtension() !== 'php') {
continue;
}

// Derive the class name from the filename.
$name = str_ireplace('.php', '', trim($fileName));
$name = str_ireplace('adapter', '', trim($name));
$class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($name) . 'Adapter';
$class = rtrim($this->classprefix, '\\') . '\\' . ucfirst($name) . 'Adapter';

if (!class_exists($class)) {
// Not namespaced
$class = $this->_classprefix . ucfirst($name);
$class = $this->classprefix . ucfirst($name);
}

// Core adapters should autoload based on classname, keep this fallback just in case
if (!class_exists($class)) {
// Try to load the adapter object
\JLoader::register($class, $this->_basepath . '/' . $this->_adapterfolder . '/' . $fileName);
\JLoader::register($class, $this->adapterfolder . '/' . $fileName);

if (!class_exists($class)) {
// Skip to next one
continue;
}
}

$adapters[] = $name;
$this->adapters[strtolower($name)] = $class;
}
}

// Add any custom adapters if specified
if (\count($custom) >= 1) {
/**
* Gets a list of available install adapters.
*
* @param array $options An array of options to inject into the adapter
* @param array $custom Array of custom install adapters
*
* @return string[] An array of the class names of available install adapters.
*
* @since 3.4
*/
public function getAdapters($options = [], array $custom = [])
{
if (\count($custom)) {
foreach ($custom as $adapter) {
// Setup the class name
// @todo - Can we abstract this to not depend on the Joomla class namespace without PHP namespaces?
$class = $this->_classprefix . ucfirst(trim($adapter));
$class = $this->classprefix . ucfirst(trim($adapter));

// If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
if (!class_exists($class)) {
continue;
}

$adapters[] = str_ireplace('.php', '', $fileName);
$this->adapters[$adapter] = $class;
}
}

return $adapters;
return array_keys($this->adapters);
}

/**
* Method to load an adapter instance
* Get an install adapter instance
*
* @param string $adapter Adapter name
* @param string $name Adapter name
* @param array $options Adapter options
*
* @return InstallerAdapter
*
* @since 3.4
* @throws \InvalidArgumentException
* @since __DEPLOY_VERSION__
*/
public function loadAdapter($adapter, $options = [])
public function getAdapter($name, $options = [])
{
$class = rtrim($this->_classprefix, '\\') . '\\' . ucfirst($adapter) . 'Adapter';
$name = strtolower($name);

if (!class_exists($class)) {
// Not namespaced
$class = $this->_classprefix . ucfirst($adapter);
if (!isset($this->adapters[$name])) {
throw new \InvalidArgumentException(sprintf('The %s install adapter does not exist.', $name));
}

if (!class_exists($class)) {
throw new \InvalidArgumentException(\sprintf('The %s install adapter does not exist.', $adapter));
if (\is_string($this->adapters[$name])) {
$class = $this->adapters[$name];

// Ensure the adapter type is part of the options array
$options['type'] = $name;

// Check for a possible service from the container otherwise manually instantiate the class
if (Factory::getContainer()->has($class)) {
return Factory::getContainer()->get($class);
}

$adapter = new $class($this, $this->getDatabase(), $options);

if ($adapter instanceof ContainerAwareInterface) {
$adapter->setContainer(Factory::getContainer());
}

$this->adapters[$name] = $adapter;
}

// Ensure the adapter type is part of the options array
$options['type'] = $adapter;
return $this->adapters[$name];
}

/**
* Method to load an adapter instance
*
* @param string $adapter Adapter name
* @param array $options Adapter options
*
* @return InstallerAdapter
*
* @throws \InvalidArgumentException
* @since 3.4
* @deprecated __DEPLOY_VERSION__ will be removed in 7.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've just noticed that the deprecation message here is wrong. If we deprecate this in 6.0, it can not be removed before 8.0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method comes from the adapter class and has been deprecated there since 1.5. It is now copied over here and directly deprecated. That should be fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then the deprecation comment should be adapted. Not sure though how a "since 3.4" fit to "deprecated 1.5".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the `since 3.4 is wrong and comes from incorrect modifications at that time.

* Use getAdapter() instead
*/
public function loadAdapter($adapter, $options = [])
{
return $this->getAdapter($adapter, $options);
}

/**
* Set an adapter by name
*
* @param string $name Adapter name
* @param InstallerAdapter|string $adapter Adapter object or class name
*
* @return boolean True if successful
*
* @since __DEPLOY_VERSION__
*/
public function setAdapter($name, $adapter)
{
if (\is_object($adapter)) {
$this->adapters[$name] = $adapter;

// Check for a possible service from the container otherwise manually instantiate the class
if (Factory::getContainer()->has($class)) {
return Factory::getContainer()->get($class);
return true;
}

$adapter = new $class($this, $this->getDatabase(), $options);
if (class_exists($adapter)) {
$this->adapters[$name] = $adapter;

if ($adapter instanceof ContainerAwareInterface) {
$adapter->setContainer(Factory::getContainer());
return true;
}

return $adapter;
return false;
}
}