PHP Manual CLI style 2.0

By | | Programming

6

Harry mentioned the handy little phpm some three years ago. And Sean Coates was kind enough to point out how it could be replaced with a shell one-liner. Doesn’t that just make one love bash?

One thing, I missed with either of the two, was the ability to see the entire manual entry. It’s quite often, that the manual actually holds useful information (Who’d known that!), so I find myself using www.php.net a lot. Or I did, until I decided to do something about it. Now, shell-scripting isn’t what I spent most of my time on, so it’s not with out a bit of pride, that I present to you phpm two-oh.


#!/bin/bash
# phpm
# commandline php-manual interface
# Kudos to Havard Eide and Sean Coates for the original idea
#
# author: Troels Knak-Nielsen <troelskn@gmail.com>
# version: 2007-11-27
#
# dependencies:
#   wget        sudo apt-get install wget
#   sed         sudo apt-get install sed
#   tidy        sudo apt-get install tidy
#   xmlstarlet  sudo apt-get install xmlstarlet
#   konwert     sudo apt-get install konwert
#   html2text   get from http://www.aaronsw.com/2002/html2text/html2text.py
#               symlink to ~/bin/html2text
#   urlencode   get from http://www.shelldorado.de/scripts/cmds/urlencode.txt
#               symlink to ~/bin/urlencode
function print_usage {
  echo "USAGE: phpm <function>"
  echo "To clear cache: phpm --clear"
  exit 0
}
# create cachedir on first run
CACHEDIR=~/.phpm
if [ ! -e $CACHEDIR ]
then
  mkdir $CACHEDIR
fi
if [ $# -gt 0 ]
then
  # parse a few options
  if [ $1 = "--clear" ]
  then
    echo "clearing cache"
    rm -r $CACHEDIR
    exit 0
  fi
  if [ $1 = "--help" ]
  then
    print_usage
  fi
  if [ $1 = "-?" ]
  then
    print_usage
  fi
  URLNAME=$(echo $1 | urlencode)
  CACHE_FILENAME=$CACHEDIR/$URLNAME
  # check cache
  if [ ! -e $CACHE_FILENAME ]
  then
    # fetch from HTTP
    HREF=http://www.php.net/manual-lookup.php?function=$URLNAME
    RESPONSE=$(wget --quiet -O - $HREF)
    if [ $? != 0 ]
    then
      echo "HTTP error" 1>&2
      exit $?
    fi
    # process response
    # test if function has direct match
    if echo $RESPONSE | grep -Eiq '<div([^>]*)class="refentry">'
    then
      # grap and format output
      # the first sed collapses blank lines, the second formats headers
      echo $RESPONSE \
        | tidy -latin1 -asxhtml --input-encoding utf8 --output-xml true --numeric-entities true 2>/dev/null \
        | xmlstarlet select --net --html -t -c "//*[@class='refentry']" 2>/dev/null \
        | tidy -latin1 --input-encoding utf8 -asxhtml 2>/dev/null \
        | konwert utf8-ascii \
        | html2text 2>&1 \
        | sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P' \
        | sed -e '/^# \(.*\)$/ { s/^# \(.*\)/\1/p ; s/\(.\{1,1\}\)/=/g }' -e '/^## \(.*\)$/ { s/^## \(.*\)/\n\1/p ; s/\(.\{1,1\}\)/-/g }' -e '/^### \(.*\)$/ { s/^### \(.*\)/\n\1/p ; s/\(.\{1,1\}\)/~/g }' \
        > $CACHE_FILENAME
    # test if there are any "best" matches
    elif echo $RESPONSE | grep -Eiq '<a href="\/manual\/en\/function[^>]*><b>'
    then
      echo "Best matches for '$1':" > $CACHE_FILENAME
      echo $RESPONSE \
        | tidy -latin1 -asxhtml --input-encoding utf8 --output-xml true --numeric-entities true --wrap 0 2>/dev/null \
        | sed -n 's/.*<a href="\/manual\/en\/function[^>]*><b>\([^<]\{1,\}\)<.*/\1/p' \
        >> $CACHE_FILENAME
    # test if there are any "weak" matches
    elif echo $RESPONSE | grep -Eiq '<a href="\/manual\/en\/function[^>]*>[^<]+'
    then
      echo "Possible matches for '$1':" > $CACHE_FILENAME
      echo $RESPONSE \
        | tidy -latin1 -asxhtml --input-encoding utf8 --output-xml true --numeric-entities true --wrap 0 2>/dev/null \
        | sed -n 's/.*<a href="\/manual\/en\/function[^>]*>[^<]\{1,\}\([^<]\{1,\}\)<.*/\1/p' \
        >> $CACHE_FILENAME
    fi
  fi
  if [ -e $CACHE_FILENAME ]
  then
    cat $CACHE_FILENAME
  else
    echo "No matches found for '$1'"
    exit -1
  fi
else
  print_usage
fi

Installing it

You obviously need a bash environment to run it (I suppose cygwin will do). Apart from that, you need a host of cli tools and utilities. If you’re on a debian based system, the following should get you running:

sudo apt-get install wget sed tidy xmlstarlet konwert

Then get the following two scripts:

Save them (Without file-extension) in ~/bin and make them executable:

chmod +x ~/bin/html2text
chmod +x ~/bin/urlencode

(Or you can put them somewhere, such as in ~/scripts and then symlink them)

Finally, save the above script as ~/bin/phpm and chmod it, like the other two scripts.

Usage

You should now be able to look up a function in the PHP manual as simple as:

phpm substr

As a bonus, you’ll get a list of suggestions for mismatches. For example:

$ phpm substring
Best matches for 'substring':
is_string
substr

Very handy, when you only remember part of the function name.

The script queries the php website for the documentation, so it’s always up-to-date. To improve performance, results are cached in ~/.phpm. You can always clear the cache by calling phpm like:

phpm --clear

Emacs bonus

As a final little bonus for the Emacs-users about, here’s a snippet for binding F4 to phpm for the current word:


(defun php-manual-lookup ()
  "Shows short documentation for the word at the point."
  (interactive)
  (let ((word (current-word t))
        (buffername "*phpm*"))
    (when (get-buffer buffername)
      (kill-buffer buffername))
    (save-excursion
      (pop-to-buffer buffername)
      (shell-command (format "phpm %s"
                             (shell-quote-argument word)) buffername)
      (other-window 1))))
(global-set-key '[f4] 'php-manual-lookup)

Learn Responsive Web Design

Join Learnable $29 Includes all SitePoint books

Troels Knak-Nielsen

Troels has been crafting web applications, as a freelancer and while employed by companies of various sizes, since around the time of the IT-bubble burst. Nowadays, he's working on backend systems for a Danish ISP. In his spare time, he develops and maintains Konstrukt, a web application framework for PHP, and is the organizer of a monthly PHP-meetup in Copenhagen.

More Posts

{ 5 comments }

Ramin November 28, 2007 at 10:45 pm

Nice script.

If you are a Windows user who has Microsoft Desktop Search installed, an alternative way to access php help would be to create a shortcut by typing the following in the Desktop Search box:
@phpm , http://www.php.net/manual-lookup.php?function=$w

Then you should be able to type in the following to get help:
phpm substring

thkoch2001 November 28, 2007 at 7:27 pm

Include it in VIM, using Tobias Schlitt’s config[1]:

" Map -H to search phpm for the function name currently under the cursor (insert mode only)
inoremap :!phpm =expand("")

[1] http://schlitt.info/applications/blog/index.php/plugin/tag/vim

thkoch2001 November 28, 2007 at 7:16 pm

Somewhere my wget is configured to send an Accept-Language header for german. First the german umlauts were not displayed correctly on my ISO-8859-1 terminal, second I don’t rely on the translated manual pages as there are sometimes outdated. So I changed line 57 to explicitly ask for english pages:

RESPONSE=$(wget --header='Accept-Language: en' --quiet -O - $HREF)

Make sure to run phpm --clear after changing!

phpimpact November 28, 2007 at 5:49 am

This is the shell-script of the year, and if you are a LAMP developer, the script of the century! Well done Troels, great use of sed :)

deminy November 28, 2007 at 3:33 am

Just tried. Nice job~~

Comments on this entry are closed.

{ 1 trackback }