Skip to main content

PHP Manual CLI style 2.0

    Troels Knak-Nielsen
    Share

    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)