As much as I love Joomla!, there is a shortcoming compared to the other two major Open Source PHP CMS, WordPress and Drupal: it doesn't come with a command-line interface like wp-cli or drush. This is a bit of a problem when you're in need of mass-provisioning sites with extensions or updates in an unattended manner. Using a CLI tool is the only way to provide a scriptable, efficient and unattended method of doing so. In this post we'll see a practical way to overcome this limitation.

Tip: If you're not interested in the technical information please skip to "TL;DR: that's how you do it".

How extension installation works

Everything that's not included in the Joomla! core is a Joomla! extension. This includes components, plugins, modules, templates, libraries, file packages and the "package" extension type which includes one or more extensions of any type. On top of that, Joomla! doesn't really discriminate between a new install and an update. It's always an installation. If the database indicates that the extension we're about to install is already installed Joomla! assumes it's an update. Therefore, no matter if we're updating an existing extension or installing a new one we just need to install the extension package file.

Extensions usually come in ZIP, tar or tar.gz/tgz archives which you install through the Extensions Manager. Actually, Joomla! cannot directly install a compressed extension package. It first needs to extract it to a temporary directory in your site's temp-folder, as defined in your Global Configuration. Thankfully there's an API for that, namely JInstallerHelper::unpack(). Therefore, we need to extract the extension package before installation and remove the temporary directory afterwards.

The PHP code to carry out these operations is quite simple:

	$packageFile = '/path/to/extension.zip';

	// Extract the package file into a temporary folder
	$package = JInstallerHelper::unpack($packageFile);
	
	if (!$package)
	{
		die('An error occurred while unpacking the file');
	}
	
	// Install the package
	$installer = new JInstaller;
	$installed = $installer->install($package['extractdir']);
	
	// Remove the temporary folder where the package was extracted to
	if (JFolder::exists($package['extractdir']))
	{
		JFolder::delete($package['extractdir']);
	}
	
	// Remove the extension package
	if (JFile::exists($package['packagefile']))
	{
		JFile::delete($package['packagefile']);
	}
	
	if ($installed)
	{
		echo "Extension successfully installed";
	}
	else
	{
		echo "Extension installation failed";
	}

The next reasonable question is exactly how do you run this PHP code from the CLI. It's obvious that it needs to run inside the context of a Joomla! application, otherwise none of the JSomething classes will be available or working properly. You can't just put the code in a .php file and have it work. Since Joomla! 1.6 this problem is solved thanks to Joomla! having added support for CLI scripts. We just need to create a JApplicationCli class in our .php file, put the PHP code in the execute() method, instantiate the object and execute it.

Of course there are a few issues with such a simplistic approach. JApplicationCli is not really designed to work with PHP CGI which is all you've got on some servers. It can't work on servers which don't have a default timezone in their php.ini files. The error detection and reporting in the sample PHP code is rather naive. Plus another few minor issues, like JInstaller being written to only work with the back-end web application of Joomla! and dying due to missing methods in JApplicationCli.

TL;DR: that's how you do it

All of these issues have been properly debugged in the install-joomla-extension.php script I wrote.

First, create a new plain text file and paste that code in it. Save it as install-joomla-extension.php in your Joomla! site's cli directory. The name is not important, the location (cli directory) is.

Now you can call it like this:

cd /path/to/site/cli php ./install-joomla-extension.php --package=/where/is/your/extension.zip

The script returns one of the following exit statuses:

  • 0. The extension was installed successfully.
  • 1. The package file was not found.
  • 3. The package file could not be extracted.
  • 250. The extension could not be installed.

You can copy this file to your site's cli directory and install extensions and extension updates any time you want.

18 comments

  • Ram Tripathi has done some of the same stuff he calls JACS (http://jandbeyond.org/program/sessions/building-joomla-cli-applications.html) for GSOC but unfortunately never seen anything released. The video looks promising.
  • This for this Nic

    Just what I needed at this moment for a Friday night session of coding and debugging an application.

    Going to make things slightly faster.
    • An important thing to note, Peter, is that this method will only work if the extension's installation script does not rely on JSession, redirections, AJAX or any JApplicationWeb / JApplicationAdmin methods. I've seen many popular extensions such as Kunena use some or all of these things, therefore making it impossible to install over CLI.
  • Great Script, thanks for sharing :) I just added a function in the ApplicationCLI
    public function getCfg($varname, $default = null)
    {
    return $this->get($varname, $default);
    }
    Just because some components are still using getCfg instead of get and ApplicationCLI doesn't have getCfg function
  • These functions were missing for me:

        public function getClientId() { return 1; }
    
        public function setUserState($key, $value) {
            $session        =& JFactory::getSession();
            $registry       =& $session->get('registry');
            if(!is_null($registry)) {
                return $registry->setValue($key, $value);
            }   
            return null;
        }   


    Still get a failed installation with plugin community builder, with this warning:

    PHP Notice:  Undefined index: HTTP_HOST in /var/www/bla.com/web/libraries/joomla/uri/uri.php on line 96
    Extension installation failed


    • HTTP_HOST and other HTTP_* constants are only available in the context of a web server. You can add a line at the top of the CLI script: define('HTTP_HOST', 'www.example.com'); where www.example.com is your domain name.

      As for extensions failing to install: some extensions do not follow the standard Joomla! installation practice of only using the installation script to perform actions. They expect to be able to display an arbitrary HTML page in the back-end through the installation message feature of Joomla!. They use that to run actions through Javascript. These extensions will fail to install via CLI or be updated using a third party service (e.g. myJoomla, Watchful or Joomla! Commander). It's up to their developers to change how the installation of these extensions works.
    • When this blog was converted from WordPress some of the formatting got lost. I only bothered going back one year and fixing formatting manually. This article was not one of them :)
  • Thanks for the script.
    Joomla's CLI seems to be buggy.
    PHP Notice: Undefined index: HTTP_HOST in /srv/http/joomla/libraries/joomla/uri/uri.php on line 96

    Error displaying the error page: Call to undefined method JoomlaExtensionInstallerCli::allowCache(): Call to undefined method JoomlaExtensionInstallerCli::isSite


    Checking these errors yields pages of debate and unresolved status.
    (Note I included define('HTTP_HOST'...)

    That extensions install in the frontend without visible error but fail in the CLI is concerning. In my case extensions installed but components did not appear in the Components menu, even though they appear in Extensions.Manage.Manage
    • I can tell you in the most categorical way that the Joomla! CLI package is not buggy. I have been one of its earliest adopters outside the Joomla! core itself. The Professional versions of Akeeba Backup, Admin Tools and Akeeba Ticket System -what I earn my living with- all use the CLI package with great success.

      The actual problem that you have is discussed in the comments above. Please read before jumping to conclusions. The real issue is that the extensions installer assumes that it is running inside a JApplicationCms instance. This is a reasonable expectation since it was never designed to run through the a CLI application.

      There are more subtle issues like that and in the fine article I tell you that I DID WRITE a working script which I share free of charge with the world. You may use it. Also please read the earlier comments here. We discussed all of that before :D

      Please note that not all extensions will install through this CLI script. This is not a limitation of Joomla but the extensions themselves. Developers are free to write pre- and post-installation scripts for their extensions. Most developers writing such scripts assume that their extensions will only ever be installed through the site's front- or backend. As I said, that's a reasonable expectation since Joomla! doesn't offer a CLI installer (yet?). This in turn means that if they are using features which hinge on JApplicationWeb, the session manager, redirections etc the installation won't work.

      The same failure caveat applies to extensions which naively assume that the post-installation message will be always displayed to the user, inside a web browser page running inside the administrator area of the site where the installation took place while being logged in as a Super User. That's what Kunena does, for example. This means that unattended / front-end installation will never work because the message is not displayed to anyone. Installation will also fail if you are installing multiple extensions at once because, again, the message is not displayed. Moreover installation will fail if you have given installation rights to lesser user groups (why would be a mystery to me as it's an invitation to privilege escalation...) and the post-installation script redirects to a page which requires Super User rights to complete its work.

      So, basically, if you have an extension which doesn't install from the CLI go file a bug report with its author. They have screwed up big time with their installation script. Take this from the guy who DID modify his installation scripts to make sure that this kind of screw-ups no longer exist and his extensions install even under CLI (or unattended installation / upgrade scripts, e.g. like MyJoomla, Watchful or Perfect Dashboard do).
  • "the guy who DID modify his installation scripts to make sure that this kind of screw-ups no longer exist"
    Not to take too much of your time but what what would an extension developer have to do to render his extension CLI friendly?
    1. Detect CLI/GUI and rout messages appropriately.
    2.Ensure HTTP_HOST and other HTTP_* constants are defined for CLI purpose.
    3. Stop reliance on HTML pages and running actions through Javascript.

    Is that it?
    • When you use JFactory::getApplication() check the type of object you get back. Don't assume it's a JApplicationWeb or JApplicationCms descendant. Therefore you can detect CLI.

      As I have explained before YOU NEVER INTERACT WITH $_SERVER VARIABLES DIRECTLY. This is something that happens when you use JUri. You don't / shouldn't need that in the CLI.

      If you're on CLI a. you don't have a time limit and b. you cannot rely on HTML, Javascript and any Joomla! API that requires the use of sessions.

      Not to put too fine a point on it, if a developer doesn't grasp these fundamental PHP and Joomla concepts there's not a cat's chance in hell that they're writing code so complex that they need a custom installation script (or that they know how to write code securely).
  • When trying installing a package via this cli script i get following error:
    "Call to undefined method JoomlaExtensionInstallerCli::isClient(): No database selected"

    Has something changed in the last Joomla Updates?
    • Yes, it does. It has to do with stuff that got deprecated around Joomla! 3.7. You can implement function isClient() { return false; } in the CLI application's class file to work around this issue. I don't use this kind of check in any of my software's post-installation scripts (it's pointless; we're installing software, why should I care if it's installed from front-end, back-end or whatnot?!) so I never cared to implement it in the CLI installer application :)
  • Thanks its a good tutorial, though I would suggest to look at this much easier to follow blog tutorial of Joomla Module installation at: (IDIOT SPAMMER'S LINK REMOVED) I hope this helps thanks.