PHP shell scripts for Magento
There are times when it’s necessary to access Magento system from outside Magento. A way to do this is using PHP shell script that bootstraps Magento. An average PHP developer would just go to Magento root directory to create Magento bootstrap file with it’s own code at the bottom. As you’ve probably guessed, there are two ways to bootstrap Magento, regular way and Magento way. In this article I’ll provide an overview of Magento way of creating PHP shell scripts.
For starters, here’s regular way of bootstrapping Magento (the one that regular Magento’s index.php is using):
require_once 'app/Mage.php';
Mage::app();
// Our own code goes here
There’s nothing wrong with this approach, but there are some downsides when compared to Magento way.
The Magento way
Now let’s move on to the second way of creating PHP shell scripts intended to interact with Magento code. If you go to path_to_magento_root/shell you will find some PHP shell scripts created in “Magento way”. For example you have indexer.php you can use to reindex by selected indexers, or compiler.php to control Magento compiler feature status. If you take a look inside these PHP shell scripts, you’ll see that they include path_to_magento_root/shell/abstract.php file containing Mage_Shell_Abstract class and that they contain classes that extend Mage_Shell_Abstract class.
Some of you might wonder how’s this better than just simply requireing Mage.php, especially if you take a look at Mage_Shell_Abstract’s constructor that does pretty much the same thing?
One of upsides of using Magento way is that Mage_Shell_Abstract class provides you with tools for parsing command line arguments with ease. Seccond thing, the Mage_Shell_Abstract class constructor will call Mage_Shell_Abstract::__applyPhpVariables() function to parse .htaccess file and apply php settings to shell script.
Even though it’s pretty much obvious how to proceed from here, I’ll paste skeleton class for a Magento PHP shell script here:
<?php
require_once 'abstract.php';
class Inchoo_Shell_Myscript extends Mage_Shell_Abstract
{
protected $_argname = array();
public function __construct() {
parent::__construct();
// Time limit to infinity
set_time_limit(0);
// Get command line argument named "argname"
// Accepts multiple values (comma separated)
if($this->getArg('argname')) {
$this->_argname = array_merge(
$this->_argname,
array_map(
'trim',
explode(',', $this->getArg('argname'))
)
);
}
}
// Shell script point of entry
public function run() {
}
// Usage instructions
public function usageHelp()
{
return <<<USAGE
Usage: php -f scriptname.php -- [options]
--argname <argvalue> Argument description
help This help
USAGE;
}
}
// Instantiate
$shell = new Inchoo_Shell_Myscript();
// Initiate script
$shell->run();
Precache PHP shell script
I’ll also point you towards PHP shell script created as an exercise, after similar functionality was required for one of our projects. This is a PHP script intended to populate cache for Magento site. The idea was to visit all Magento URLs using Zend_Http_Client (both product canonical and category based URLs as well as all category URLs). This script allows you to select which stores and categories should be processed using command line arguments. Here’s download link:
And here’s link to Inchoo_Precache Magento shell script’s GitHub repository.and link to Inchoo_Precache Magento shell script’s GitHub repository:
Inchoo Precache GitHub Repository
And here are the usage instructions available using php -f precache.php -- help
command from terminal inside shell directory of your Magento root after adding precache.php to your Magento installation:
Usage: php -f precache.php -- [options]
--stores <names> Process only these stores (comma-separated)
--categories <names> Process only these categories (comma-separated)
help This help
Magento way is the way to go, don’t you agree? Happy coding!
11 comments
HI Marko,
I have made similar script and i have also used your script. but i have 1 problem. my website is only available for register customer so if customer is not logged in he will redirect to login page. so i get 302 or 401 Http status.
Do you have any solution for this problem? i want to make login customer before run script. i have try to do by customer id but its not help.
Thanks
You obviously forgot to call:
} else {
echo $this->usageHelp();
}
in the run() Method, when no arguments are passed.
Please add the quote after Mage.php in :
require_once ‘app/Mage.php;
Mage::app();
// Our own code goes here
Thanks Sébastien,
very kind of you to report, it’s fixed now.
Regards,
Marko
@Marko
Would it be possible to quicly re-write this script. Take all the good that is already here. But then replace the category indexing by sitemap indexing. This way the flow would be
– get list of stores store
– per store
– find the corresponding sitemap.xml
– visit every URL
Do you think this can be done easily? Love to hear
I am getting the following error while trying to run this script
PHP Fatal error: Cannot break/continue 1 level in /var/www/html/shell/precache.php on line 150
I have looked up the error and it has been pointed out that you cannot break out of an IF statement.
Nice Tutorial and working! It would be great, if the precache script could be run in background without using the webserver. Running the scripts without the httpd gives me error:
Unable to Connect to tcp://mydomain.example:80. Error #111: Connection refused…
The following snippet for example (reading order details) uses only Magento API and it can be started without runnning httpd:
@Markus
Hello! What this script does is visits pages to generate cache and this isn’t possible without web server running.
Regards,
Marko
And one more comment. Super thanks btw!!
How do I know if it has been or is running? I dont see anything at the command line.
Major thanks Marko
@Overhemden
Hi. The script should actually display verbose information on command line while running. If this isn’t the case then something is wrong.
Regards
Thanks – this is quite handy. To share some experiences. We have had such a script and realized that some pages have many rewrites. And as a result caching took hours for 99% of page urls that hardly generate any traffic.
The solution was to capture the sitemap.xml (location can be grabbed dynamic per store from code) and this was parsed and every page grabbed.
Let me know what you tink