PHP Manual CLI style 2.0

Tweet

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