Geoff Garbers

Husband. Programmer. Tinkerer.

Implementing an AppShell for CakePHP

May 02, 2010

UPDATE: Since the release of the later versions of CakePHP (I think version 1.3.0 and onwards), there are the APP/libs libraries that we can now utilize. So, instead of having two separate app_shell.php files, you can now define APP/libs/app_shell.php, and call App::import('Lib', 'AppShell'); at the beginning of your shell files.

If you’re like me, you like using CakePHP for all your web development in PHP, because it’s so… convenient. All those automagically implied model bindings and relationships. Yays!

Another great feature is the fact that all the controllers and models are descended from AppController and AppModel, which means that you can have some common functionality between all the controllers and models in your application. I find this very handy. However, what happens when you want to use the same thing in shell scripts?

I was wanting to do this very thing recently, for a project at work. I needed to interact with a number of XML APIs in order to download the latest news stories and information, and save them in a local database. Different shelsl were used for different subjects - NewsShell for news stories, MultimediaShell for multimedia items, etc.

Now, the thing is, each of these needed to connect to a feed, but I didn’t want to have to copy and paste the code used to fetch the data multiple times. So, I decided to make my own implementation of an AppShell.

It’s not as nice and tidy as Cake’s core implementation of AppController and AppModel, but it does the trick.

The Implementation

Let’s say I have a shell that will download the latest news feed from a site. Normally, I would implement it like this (and duplicate the same structure for each shell script that requires the fetching of information):

class NewsShell extends Shell
{
    private function _getData($url)
    {
        // logic to retrieve data from xml feeds here
    }

    public function updateAllNews()
    {
        $news = $this->_getData('http://web.address/news.xml');
        // process the rest here
    }
}

However, this is how I implemented my AppShell implementation: Firstly, create AppShell in VENDORS/shells/app_shell.php:

class AppShell extends Shell
{
    protected function __getData($url)
    {
        // logic to retrieve data here
    }
}

Then, you’ll need to modify the way the NewsShell was implemented:

require_once 'app_shell.php';
class NewsShell extends AppShell
{
    public function updateAllNews()
    {
        $news = $this->__getData('http://web.address/news.xml');
        // process the rest here
    }
}

And that, dear people, is my basic implementation of a CakePHP AppShell. Unfortunately, there are a few limitations on this process. For instance, if you use your shells in the root VENDORS directory, and implement some other shell scripts in APP/vendors/shells/, then they won’t have access to AppShell.

I would suspect that this is one of the reasons why AppShells haven’t yet been implemented in Cake. It is definitely something that I would find incredibly useful. Comments? Questions?