Using PHP CLI for TextMate commands | Comments (3)
Posted in Code on 19th March 2007, 11:51 pm by Stuart
TextMate comands are very flexible ways to harness the power of the commandline. In previous commands that I have written I have tended to use BASH directly but one of the clever things about TextMate is that it allows you to use pretty much any language that exists on your mac.
As an example PHP on CLI is not that likely to be your first choice when you think about working on the command line. In fact the majority of bundles make use of Ruby as this is a very sleek programming language and has some nice touches to facilitate it’s use on the command line. However for this article I am going to simply outline how to make use of PHP from within textmate and look at a very basic but potentially very useful (to JS programmers) example.
First of all to start a php command we have two options:
- To use php with the -r switch so that we can write simple code without the need for php code blocks
- To use php as a shell script with a shebang and the opening and closing code blocks.
Personally unless you are doing a very simple one-liner you are probably best to stick to the latter which is ideal for creating shells scripts in PHP.
To start, first you need to open the bundle editor, create a new command and start with the PHP bang. This is a way of indicating that we want the following script to be read by PHP. This looks like this:
#!/usr/bin/php
Where /usr/bin/php is the path to your php binary
Environment Variables
All of the special environment variables are available to TextMate via the $_ENV array. This makes accessing them a breeze.
So as a starting point let’s print out the $_ENV array in textmate so we can see what’s available. Set-up the command window as follows:
- Save:
- Nothing
- Command(s)
#!/usr/bin/php
<?php
print_r($_ENV);
?>- Input
- None
- Output
- Create New Document
You should see output similar to the following (some entries removed to save space):
[PATH] => /usr/bin:/bin:/usr/sbin:/sbin
[DIALOG] => /Applications/TextMate.app/Contents/PlugIns/Dialog.tmplugin/Contents/Resources/tm_dialog
[TM_LINE_NUMBER] => 1
[TM_CURRENT_LINE] =>
[TM_SOFT_TABS] => YES
[TM_TAB_SIZE] => 2
[BASH_ENV] => /Users/scol/Library/Application Support/TextMate/Support/lib/bash_init.sh
[TM_SUPPORT_PATH] => /Users/scol/Library/Application Support/TextMate/Support
[TM_SCOPE] => text.plain
[TM_ORGANIZATION_NAME] => __MyCompanyName__
[TM_BUNDLE_PATH] => /Users/scol/Library/Application Support/TextMate/Bundles/JSMin.tmbundle
[TM_MODE] => Plain Text
[TM_COLUMN_NUMBER] => 1
[TM_LINE_INDEX] => 0
[TM_COLUMNS] => 190
[TM_BUNDLE_SUPPORT] => /Users/scol/Library/Application Support/TextMate/Bundles/JSMin.tmbundle/Support
[SHLVL] => 1
Now we can simply access these vars with getenv e.g. getenv('TM_TAB_SIZE'); or by directly looking at the $_ENV array $ENV['TM_TAB_SIZE'];
Reading Standard Input
Now another thing that’s needed to be able to do anything in a textmate command is accessing STDIN. STDIN is the standard input (who’d have thunk it?!) to the command and it’s that which is set-up through the command window. The current main options are:
- None
- Selected Text
- Entire Document
Note: If you select “Selected Text” fallbacks to word, line, scope, document, character or nothing are also available.
So if we have “Selected Text” as out input this will be fed through to our command as the Standard input. In addition we also can make use of the $_ENV variable TM_SELECTED_TEXT too but for this example I’ll stick to demonstrating STDIN.
Within PHP CLI STDIN is a constant that points to an already open stream resource. This allows you to read in the stdin line by line or as you see fit. To get the entire STDIN instead one needs to use fread and supply the number of bytes to be read. To do this precisely you can use fstat to work out the size of the resource and therefore set a variable equal to the contents of STDIN in it’s entirety as follows:
$fstat = fstat(STDIN);
$stdin = fread(STDIN,$fstat['size']);
Now that we have STDIN we can now do something with it. This example is a follow on from the small bundle I had created to carry out JavaScript Minification with JSMin. This script takes a selected set of external (but local) JavaScript file references from an HTML file and runs those files through JSMin which is contained within the Bundle support files. This code would also be easy to simply modify to use a local copy of JSmin if this is more convenient.
So in your HTML file you would select your script elements that look something like this (Note: this will only work with locally referenced files for obvious reasons):
<script src="../yui/calendar.js" type="text/javascript"></script>
<script src="../yui/connection.js" type="text/javascript"></script>
<script src="../yui/container.js" type="text/javascript"></script>
When we call our command the selection is piped into the front of the command as the standard input.
Here’s the code that deals with the input:
#!/usr/bin/php
<?php
$fstat = fstat(STDIN);
$stdin = fread(STDIN,$fstat['size']);
preg_match_all(’/src=["\'](.*?)[\'"]/’, $stdin, $m, PREG_SET_ORDER);
if (is_array($m)){
$j = count($m);
$files = ”;
for ($i=0; $i < $j; $i++){
$files .= $m[$i][1].’ ‘;
}
}
echo shell_exec(’cat ‘. $files . ‘|’.str_replace(’ ‘, ‘\\ ‘, $_ENV['TM_BUNDLE_SUPPORT']).’/bin/jsmin’);
?>
What this simply does is to create an array of all matches to the src attribute where the backreference matches the contents of the src attribute. Once we have that, it’s possible to build up a string of all of the path names, cat (concatenate) the files together and run the result through JSMin. By setting the command to create a new file you can simply save the file from TextMate and give it the name of your choice.
Have at it and let me know if you spot any ways to improve the examples or have created some cracking commands using PHP in TextMate.
To try this example download the JSMin Bundle which contains the example above along with some other ways to use JSMin. This bundle contains a pre-compiled universal binary of JSMin already. Once you have unpacked the zip simply double click it to install it into TextMate.

Very nice. Though I think someone ate at least one backslash in your code snippet. (The str_replace part at the end?)
I also like TextMate for learning Ruby in such situations and try not to resort to using PHP as a language for commands :).
@Soryu: Well spotted on the code example. That’s been fixed now.
I know what you mean about not resorting to using PHP within textmate it feels strange in some respects. However the point of this article was to show that if you like PHP then it’s possible to use PHP for commands and that the ENV vars are still available.
From my own point of view I’m starting to use Ruby and Python for Textmate commands and particularly Python for a few other things. Hopefully from now on that will mean I’m down with the cool kids
[...] As Einars has pointed out this functionality is part of the built-in JavaScript Bundle under “Reformat Document”. I’ll leave the rest of this post here as it does provide an example of how to set-up a command with an existing PHP script and if you’re not a TextMate user then this will work as a CLI script too and you can find more info on using PHP for CLI in TextMate here [...]