PHP Manual CLI style 2.0

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).*n1/d; s/n//; h; P' 
        | sed -e '/^# (.*)$/ { s/^# (.*)/1/p ; s/(.{1,1})/=/g }' -e '/^## (.*)$/ { s/^## (.*)/n1/p ; s/(.{1,1})/-/g }' -e '/^### (.*)$/ { s/^### (.*)/n1/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)

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • deminy

    Just tried. Nice job~~

  • phpimpact

    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 :)

  • thkoch2001

    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!

  • thkoch2001

    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

  • Ramin

    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

  • Pingback: PHP help at your command line | VT's Tech Blog