Magento Quickies

All the Non-Trivial Magento Trivia

0 notes

N98-magerun: Understanding PHAR Files

Throughout this series we’ve skirted around one important issue. Namely, the opaque nature of PHP’s phar format.

Many groups claim the term “open source” in someway or another. There’s open source the license, which dictates how and where code can be used and shared. Then there’s open source “you can you see and read the literal source code”, which may or may not have an open-source license. Many (of the better) commercial Magento extensions fall into the later category: They’re distributed as plain text files, but the extension vendor still asserts copyright over the files.

While the n98-magerun is open source both in their licensing and their literal source code, PHP’s phar format muddies the waters a bit. A phar is, by default, not readable source code.

Fortunately, it’s relativly eay to peek inside any phar and see the source files it was created from, which is what we’ll show you in today’s article.

What is a phar?

A phar is a PHP Archive file. If you’re familair with the java programming language, phar files are (were?) an attempt to bring a java’s jar concept into PHP. Or as the official documentation puts it.

What is phar? Phar archives are best characterized as a convenient way to group several files into a single file. As such, a phar archive provides a way to distribute a complete PHP application in a single file and run it from that file without the need to extract it to disk. Additionally, phar archives can be executed by PHP as easily as any other file, both on the commandline and from a web server. Phar is kind of like a thumb drive for PHP applications.

That’s all nice conceptually — but what does it mean? If you open up the n98-magerun.phar file with text editor, you’ll see something like this

#!/usr/bin/env php
<?php

Phar::mapPhar('n98-magerun.phar');

$application = require_once 'phar://n98-magerun.phar/src/bootstrap.php';
$application->setPharMode(true);
$application->run();

__HALT_COMPILER(); ?>

[gobs of binary-ish looking data]

So, there’s some standard PHP near the top, but then it’s a mumble of mixed binary and ASCII data all the way down. What are we looking at?

Internally, phar archives can collect their files in the zip format, or the tar format. In addition to that the entire archive, as well as individual files in the archive, may be compressed in the gzip or bzip2 format. That’s what all the partially muddled lines are — an archive of every file in the phar.

So what about the stuff near the top?

#!/usr/bin/env php
<?php

Phar::mapPhar('n98-magerun.phar');

$application = require_once 'phar://n98-magerun.phar/src/bootstrap.php';
$application->setPharMode(true);
$application->run();

__HALT_COMPILER();

This is the archive’s stub file. Unlike java and jars, PHP has no history or concept of a class having a main function to run. If you want your phar to be an executable program (as opposed to a portable library), you need to use a stub file.

The phar stub file is a small piece of PHP code used to initilize your application. The n98-magerun stub file can be found in _cli_stub.php.

#File: _cli_stub.php
#!/usr/bin/env php
<?php

Phar::mapPhar('n98-magerun.phar');

$application = require_once 'phar://n98-magerun.phar/src/bootstrap.php';
$application->setPharMode(true);
$application->run();

__HALT_COMPILER();    

As you can see, it’s identical to what ends up in the n98-magerun.phar file. The mechanics of phar creation are beyond the scope of this article, but if you wanted to get started with your own archives take a look at the n98-magerun phing build.xml file, particularly the pharpackage task.

Opening up a phar

Having looked inside the n98-magenrun.phar file, we now know that a phar is a tiny bit of PHP code (the stub), combined with a messy binary-ish blob. Depending on your parituclar security needs, trusting the code that’s in that binary-ish blob may or may not fly in your organization.

Foruntly, this isn’t unreadable executable machine code. As we mention earlier, these files are zip or tar archives. As with those archive formats, the files from a phar can be easily extracted. The code to do so is built right into PHP. Just create a simple CLI script with the following content

#File: unphar.php
$phar = new Phar('/path/to/n98-magerun.phar');
$phar->extractTo('/path/to/extract');

This instantiates a Phar object (PHP’s internal representation of a phar archive), and then uses its extractTo method to extract the files to a specific folder. If you run the above script, and then take a look at the folder you extracted to

$ ls -l /path/to/extract
-rw-rw-rw-  1 username  staff  1085 Jun 11 14:27 MIT-LICENSE.txt
-rw-rw-rw-  1 username  staff  3807 Jun 11 14:27 config.yaml
drwxr-xr-x  6 username  staff   204 Jun 11 14:27 res
drwxr-xr-x  4 username  staff   136 Jun 11 14:27 src
drwxr-xr-x  9 username  staff   306 Jun 11 14:27 vendor

you’ll see a directory structure that contains all the files that were included in the phar.

Decompressing

There’s one last step you may need to take before you may start examining the contents of your phar archive. If you use cat to view the contents of a file, you’ll be in for a surprise.

$ cat /path/to/extract/config.yaml
????P?Ј ZL7?{?R???I?2f?>??<?y?-?3d?h?߂CT.?A~??+?T4v?ѐ44h?9?d?a0L!?# ?h?
...

For some reason, when PHP unarchives n98-magerun.phar, it fails to decompress the files. You can confirm this with OS X’s file command, which identifies files of unknown types.

$ cd /path/to/extract/
$ file config.yaml 
config.yaml: bzip2 compressed data, block size = 400k

I’ve done outsourced some detective work on this, but havne’t been able to get to the bottom of it. The Phar class has a decompressFiles method. My assumption is this will decompress the files in the archive. However, this tripped up with the following error

PHP Fatal error:  Uncaught exception 'BadMethodCallException' with message
'unable to write contents of file    
"vendor/fzaninotto/faker/src/Faker/Provider/sr_Latn_RS/Person.php" to new phar 

If anyone’s succesfully unphared and decompressed these files strictly via PHP, please let me know. In the meantime, you’ll want to decompress each individual file manually before performinig yoru code review. I did it by writing this quick PHP command line sciript

#File: unbzphar-file.php
&lt;?php
namespace Pulsestorm\Cli\Unbzphar;
function main($argv)
{
    $script = array_shift($argv);

    echo "starting loop";    
    foreach($argv as $file)
    {
        echo "decompressing file $file with bunzip2 --stdout";
        $contents = `bunzip2 --stdout $file`;
        file_put_contents($file, $contents);
    }
    echo "ending loop";
}
main($argv);

and then using find and xargs to run it on every file in the decompressed archive folder

$ find /path/to/extract -type f | xargs php /path/to/unbzphar-file.php 

The unbzphar-file.php script runs each passed in filename through bunzip2 --stdout (which decompresses it), and then immediatly rewrites the file with the returned content.

The find /path/to/extract -type f command finds everything in a directory that’s a file (as opposed to a sub-directory).

Then, we pipe the output for this command through xargs. The xargs program takes an (almost) unlimited number of lines from standard output, and then uses them as individual arguments to a specific unix command. In out case, that specitic unix command is

php /path/to/unbzphar-file.php 

and viola! All our files are decompressed.

You’re now ready to fully examine any phar’s contents, and ensure you know exactly what you’re getting into.

Filed under magento n98magerun

0 notes

Magento Inc. Decides EE 1.13 is not Ready for Production

On one hand it’s good to see a company candidly admit a mistake to its customers. On the other hand this isn’t the first time a version’s been rushed out the door so there’s something to talk about at Magento Imagine.

The next year or two is going to be interesting from a Magento engineering point of view: Does the platform develop into something like Drupal with an active development community, or does it go the way of SugarCRM — a relevant platform that is, for all intents and purposes, done.

0 notes

Magento 1.13 and MySQL Triggers

I know “no more consulting” was a great business move for Varient/Magento Inc./etc., but the folks building Magento’s core are slowly loosing touch with the day-to-day needs of working developers.

I’m glad there’s folks like Robert willing to suffer the slings and arrows of unfinished features and share solutions with the rest of us. Here’s his entire tweet storm on the problems with triggers in PHP web applications.

Filed under magento-enterprise

0 notes

Alan Storm: Magento Admin Hello World Revisited

I rewrote my original Magneto admin console hello world article to be more, um, correct.

My original Magento articles weren’t based on a mystical deep knowledge of the system — they were plain old trial, error, and analysis. For the most part I figured out what the core team was on about, but every so often I’d come up with something that worked but was a massive misuse of the system. Using a custom front name for admin pages was one of those mistakes. We regret the error.

Filed under magento

2 notes

Magento Connect Bookmarklet

Stop me if this sounds familiar.

  1. Hey look, a Magento Connect extension, let’s try it out

  2. Oh, right, I need to be logged in to get the extension key

  3. Where’s that password again? [knocks over pills]

  4. Waiting for Magento site to log me in

  5. [Still waiting]

There are, of course (I assume), reasons for Magento to force a sign-in before letting you grab a free extension key. That said, four plus years (!) in it’s still one of the things that annoys me about the platform. So here’s a bookmarklet that’ll extract the Connect 2.0 key from an extension page, even if you’re not logged in.

Visit the bookmarklet landing page, drag the Connect Key link to your bookmarks bar, click on it while you’re looking at a Magento Connect page, and a text field with a link will be appended at the top of the page. Tested in Chrome/OS X, should work elsewhere, bug reports welcome Your millage mary very, PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, etc.

Filed under magento

1 note

Magento CSRF Protection

Maybe if I write this down I’ll remember it.

You probably know all Magento admin console URLs need a nonce/key in the URL

http://magento.example.com/admin/catalog_product/edit/id/174/key/c4df66cd2118cb5422c9fb5eff7eq4f0/

That’s why we use the Mage::getModel('adminhtml/url') model object to generate URLs.

What I always forget is any POST to Magento’s backend also needs a form_key variable. Double the CSRF protection. Without this a POST will be redirected to the dashboard. You can generate a form_key with

Mage::getSingleton('core/session')->getFormKey()

Filed under magento