PX : code

Dictionary client class package by Jesus M. Castagnetto
Download this code


A base class and 2 derivated classes (pkg.dict.php) to generate clients for
dictionary servers that follow the DICT protocol (RFC 2229), such as the
server at dict.org 

Included also 2 example of query interfaces generated using this classes, one
performs a simple search (simplesearch.php), and the other shows how to get
information from the dictionary server and use that to generate a form
allowing for more complex searches (extendedsearch.php).  

[UPDATE: 2000/06/05] Fixed some bugs reported by Davor Cengija
<davor@croart.com> and found another bug related to URLs referred in the
definition that are also marked as crossrefence terms 

[UPDATE:  2000/06/06] Check for duplicates when creating the links, and
modified the external URLs regex to recognize more protocols, as well as
eliminate crossrefences to RFC's

[UPDATE: 2000/06/07] Fixed the missing </form> tag in the examples, thanks
to Stoyan Jekov <jekov@cig.nml.mot.com> for finding the omission.

NOTE: Separate the 3 files before using the examples.


::::::::::::::
pkg.dict.php
::::::::::::::
<?php

/* 
 * This set of classes implement a subset of the DICT protocol
 * and it is meant to be use to generate clients that
 * query a DICT server
 *
 * By default, the class uses the dict.org server in
 * the port 2628.
 *
 * (c) 2000, Jesus M. Castagnetto <jmcastagnetto@zkey.com>
 * 
 * License: GPL, see http://www.gnu.org/copyleft/gpl.txt
 * 
 * Changes:
 *
 * 2000/06/04 - added the correct link to the GPL license
 * 2000/06/05 - added the in_array() method to the base class
 *              from a suggestion by Davor Cengija <davor@croart.com>
 *              who pointed out that PHP gained the in_array()
 *              function in 3.0.12, and 4.0.0
 */

//============================================================ 

/*
 * class DictBase
 *
 * Base class for implementing the DICT protocol to communicate
 * with dictionary servers. It defaults to dict.org and port 2628.
 */


class DictBase {

    var 
$host "dict.org";
    var 
$port "2628";
    var 
$max_length 6144;  // 1024 * 6 to cover UTC8 chars
    
var $socket;

    var 
$valid_codes =
        array ( 
            
110111112113114130150151152
            
210220221230250,
            
330,
            
420421,
            
500501502503530531532,
            
550551552554555
        
);

    var 
$return_code = array();
    
    function 
DictBase() {
        
// empty constructor
    
}
    
    function 
set($var$val) {
        
$this->$var $val;
    }

    function 
get($var) {
        return 
$this->$var;
    }

    function 
parse_code($str) {
        
ereg("^([0-9]{3}) (.+)"$str, &$reg);
        
$error = ( $reg[1] >= 300 );
        
$this->return_code = array(    "error"=>$error
                                    
"code"=>$reg[1], 
                                    
"desc"=>$reg[2]);
    }

    function 
is_valid_code() {
        return 
$this->in_array($this->return_code["code"], $this->valid_codes);
    }

    function 
is_error_code() {
        return 
$this->return_code["error"];    
    }
    
    function 
print_code() {
        
$out $this->is_error_code() ? "<ERROR> " "";
        
$out .= "[".$this->return_code["code"]."] ".$this->return_code["desc"]."\n";
        echo 
$out;
    }

    function 
connect() {
        
$fp fsockopen($this->host$this->port, &$errno, &$errstr90);
        if (!
$fp) {
            echo 
"Cannot connect: ".$errno." = ".$errstr."\n";
            exit;
        } else {
            
$this->socket $fp;
        }
    }

    function 
close() {
        
fputs($this->socket"QUIT\r\n");
        
$tmp fgets($this->socket$this->max_length);
        
fclose($this->socket);
    }

    function 
read_data() {
        while (
$read fgets($this->socket$this->max_length)) {
            if (
ereg("^\.\r\n$",$read))
                break;
            
$out .= $read;
        }
        return 
$out;
    }

    
// To support old PHP3 versions ( older than 3.0.12 )
    // A point I forgot and Davor Cengija reminded me about
    
function in_array($element$arr) {
        
// figure out version
        
list($major$minor$release) = explode("."phpversion());
        if ((
$major == && $relesase >= 12) || $major == 4) {
            return 
in_array($element$arr);
        } else {
            
// assumes that we want to compare element value
            
while (list($key$val) = each($arr)) {
                if (
$val == $element)
                    return 
true;
            }
            return 
false;
        }
    }

// end of base class Dict

//============================================================ 

/*
 * Class DictServerInfo
 *
 * To generate objects containing DICT server information.
 * Extends the DictBase class.
 */


class DictServerInfo extends DictBase {

    var 
$info = array();

    function 
DictServerInfo($host=""$port=""$extended=false) {
        
$this->init($host$port$extended);
    }

    function 
init($host$port$extended) {
        if (
$host
            
$this->set("host"$host);
        if (
$port)
            
$this->set("port"$port);

        
$this->connect();

        
// get connection response line
        
$line fgets($this->socket$this->max_length);
        
$this->parse_code($line);
        if (
$this->is_error_code()) {
            
$this->print_code();
            exit;
        }
        
// extract capabilities info from response line
        
ereg("^[0-9]{3} (.*) <([^<]*)> <(.*)>"$line, &$reg);
        
$this->info["signature"] = $reg[1];
        
$this->info["capabilities"] = explode("."$reg[2]);
        
$this->info["msg-id"] = $reg[3];
        
        
// get description on the server and store verbatim
        
$this->info["server"] = $this->show("SERVER");

        
// get the dbs and strategies for this server
        
$dbs $this->show("DB");
        
$this->store("databases",$dbs);
        
$strats $this->show("STRAT");
        
$this->store("strategies",$strats);

        
// get the description of each database
        // if extended info is requested
        
if ($extended)
            
$this->get_dbs_info();

        
// close the connection
        
$this->close();
        }

    function 
show($str) {
        
fputs($this->socket"SHOW ".$str."\r\n");
        
$tmp chop(fgets($this->socket$this->max_length));
        
$tmp2 explode (" "$tmp);
        if (
$str == "DB")
            
$this->info["num_dbs"] = (int) $tmp2[1];
        if (
$str == "STRAT")
            
$this->info["num_strat"] = (int) $tmp2[1];
        
$data $this->read_data();
        
$tmp fgets($this->socket$this->max_length);
        return 
$data;
    }

    function 
store($str$data) {
        
$arr explode("\r\n"$data);
        
$out = array();
        for (
$i=0$i<count($arr); $i++) {
            if (
chop($arr[$i]) == "")
                continue;
            
ereg("^([^ ]+) \"?([^\"]+)\"?"$arr[$i], &$reg);
            
$out[$reg[1]] = $reg[2];
        }
        
$this->info[$str] = $out;
    }

    function 
get_dbs_info() {
        
$ndb $this->info["num_dbs"];
        
$dbs $this->info["databases"];
        
$dbinfo = array();
        while (list(
$k$v) = each($dbs)) {
            
$dbinfo[$k] = $this->show("INFO ".$k);
        }
        
$this->info["dbs_desc"] = $dbinfo;
        
    }

    function 
get_info ($str) {
        return 
$this->info[$str];
    }

// end of class DictServerInfo

//============================================================ 

/*
 * class DictQuery
 *
 * To create query objects to search a DICT server
 */

class DictQuery extends DictBase {

    var 
$term "";
    var 
$method "exact";
    var 
$searchdb "*";
    var 
$query_type "DEFINE";
    var 
$valid_methods = array ("exact""prefix""substring""suffix",
                                
"re""regexp""soundex""lev");
    var 
$result = array();
    var 
$numres 0;

    function 
DictQuery($host=""$port="") {
        
$this->init($host$port);
    }
    
    function 
init($host$port) {
        if (
$host
            
$this->set("host"$host);
        if (
$port)
            
$this->set("port"$port);
        
        
$this->connect();
        
// get connection response line
        
$line fgets($this->socket$this->max_length);
        
$this->parse_code($line);
        if (
$this->is_error_code()) {
            
$this->print_code();
            exit;
        }
    }


    function 
search($term$method$db) {
        if (!
$this->is_method($method)) {
            echo 
"**ERROR** invalid method: ".$method."\n";
            exit;
        }
        
$this->clear_results();
        
$this->term $term;
        
$this->method $method;
        
$this->searchdb $db;

        
$query = ($method=="exact") ? "DEFINE $db " "MATCH $db $method ";
        
$query .= "\"".$term."\"\r\n";
        
        
fputs($this->socket$query);
        
$line fgets($this->socket$this->max_length);
        
ereg("^[0-9]{3} ([0-9]+) .+"$line, &$reg);
        
$this->numres = (int) $reg[1];

        if (
$method != "exact") {
            
$rlist $this->read_data();
            
$this->result explode("\r\n"chop($rlist));
        } else {
            
$regex "^[0-9]{3} \"([^\"]+)\" ([^\" ]+) \"([^\"]+)\"";
            
$allres = array();
            
$entry = array();
            for (
$i=0$i<$this->numres$i++) {
                
$line chop(fgets($this->socket$this->max_length));
                if (
$line == "")
                    continue;
                
ereg($regex$line, &$reg);
                
$entry["term"] = $reg[1];
                
$entry["dbcode"] = $reg[2];
                
$entry["dbname"] = $reg[3];
                
$entry["definition"] = $this->read_data();

                
$this->result[$i] = $entry;
            }
        }
    }    


    function 
define($term$db="*") {
        
$this->search($term"exact"$db);
        
$this->close();
    }

    function 
match($term$method="prefix"$db="*") {
        
$this->search($term$method$db);
        
$this->close();
    }

    function 
is_method($method) {
        return 
$this->in_array($method$this->valid_methods);
    }

    function 
clear_results() {
        
$this->numres 0;
        
$this->result = array();
    }

// end of class DictQuery


?>


::::::::::::::
simplesearch.php
::::::::::::::
<!--
  Simple example that uses pkg.dict.org
  (c) 2000, Jesus M. Castagnetto <jmcastagnetto@zkey.com>
  License: GPL, see www.gnu.org/copyleft/gpl.txt

  Changes:
  2000/06/04 - Added the correct link to the GPL license
  2000/06/05 - Fixes to bugs reported by Davor Cengija <davor@croart.com>
               (1) Links that look like {{UNIX}} were not being stripped
                   correctly
               (2) Crossreference terms sometimes spanned a line break
                   which looked real ugly/bad
               Thanks to Davor for reporting the bugs in this example
  2000/06/05 - Found another bug, some crossreferences are to URLs and
               not terms in the dictionaries, the makeLink() function has
               been fixed to account for that.
  2000/06/06 - Check for duplicates when creating the links, and modified
               the external URLs regex to recognize more protocols, as well
               as eliminate crossrefences to RFC's
  2000/06/07 - Stoyan Jekov <jekov@cig.nml.mot.com> has sharp eye, and 
               noticed that I forgot to close my HTML FORM element
-->
<?php 
    $start
=time(); 
    include (
"./pkg.dict.php");
?>
<html>
<head>
<title>Simple Form to Query dict.org</title>
</head>
<body>
Search the dict.org server
<p>
<form action="<?php echo $PHP_SELF ?>" method="post">
<input type="text" name="query_term" size="60" 
<?php
if ($query_term)
    echo 
"value=\"".$query_term."\"";
?>
><br>
<input type="hidden" name="database" value="*">
<input type="hidden" name="strategy" value="exact">
<input type="submit" name="submit" value=" Search "> 
<input type="reset" name="reset" value=" Clear form input ">
</form>
<hr>
<?php

// check if element is in the array

function inArray($element$arr) {
    
// figure out version
    
list($major$minor$release) = explode("."phpversion());
    if ((
$major == && $relesase >= 12) || $major == 4) {
        return 
in_array($element$arr);
    } else {
        
// assumes that we want to compare element value
        
while (list($key$val) = each($arr)) {
            if (
$val == $element)
                return 
true;
        }
        return 
false;
    }
}

// remove duplicates from array
// and eliminate the patterns in $nolinks

function cleanArray($arr) {
    
$nolinks "rfc:";
    
$out = array();
    for (
$i=0$i<count($arr); $i++)
        if (!
inArray($arr[$i], $out) && !ereg($nolinks$arr[$i])) 
            
$out[] = $arr[$i];
    return 
$out;
}


//make the links to other words in the description

function mkLinks($str$db) {
    global 
$PHP_SELF;
    
    
// modified the regexes to fix the bug reported by <davor@croart.com>
    
$patt "\{+([^{}]+)\}+";
    
$regex "<b>\\1</b>";
    
$out ereg_replace($patt$regex$str);
    
$patt "/\{+([^{}]+)\}+/";
    
preg_match_all($patt$str, &$reg);
    
$link $reg[1];
    
// clean up array
    
$link cleanArray($link);
    if (
count($link) > 0)
        
$out .= "<i>See also:</i>\n";
    
    for (
$i=0$i<count($link); $i++) {
        
// added the line below to fix a second bug reported by <davor@croart.com>
        
$link[$i] = ereg_replace("[[:space:]]+"" "$link[$i]);
        
// observed myself another bug with references to URLs - JMC
        // check if it is a HTTP URL or a crossrefence
        
$protocols "(http|https|ftp|telnet|gopher)://|(mailto|news):";
        if (
ereg($protocols$link[$i])) {
            
// parse the link and mark it using <>
            
$prot1 "^((mailto|news):.+)$";
            
$prot2 "(http|https|ftp|telnet|gopher)://";
            
$prot2 "^(.*) *\((".$prot2.".+)\)$";
            if (
ereg($prot1$link[$i], &$regurl)) {
                list (
$tmp$url) = $regurl;
                
$desc $url;
            } elseif (
ereg($prot2$link[$i], &$regurl)) {
                list (
$tmp$desc$url) = $regurl;
                if (
$desc == "")
                    
$desc $url;
            }
            
$out .= "&lt;<a href=\"".chop($url)."\" target=\"_blank\">";
            
$out .= chop($desc)."</a>&gt; ";
        } else {
            
$out .= "[<a href=\"".$PHP_SELF."?query_term=";
            
$out .= urlencode($link[$i])."&database=".urlencode($db);
            
$out .= "&strategy=exact\">".$link[$i]."</a>] ";
            if ((
$i 5) == && $i 0)
                
$out .= "\n";
        }
    }
    
$out .= "\n";
    return 
$out;
}

function 
parr($arr) {
    echo 
"<ul>";
    while (list(
$k,$v) = each($arr)) {
        if (
gettype($v) == "array") {
            echo 
"<ul>";
            echo 
"* $k , new array*<br>";
            
parr($v);
            echo 
"</ul>";
        } else {
            echo 
"$k = $v<br>";
        }
    }
    echo 
"</ul>";
}


// perform a query to the server

function doQuery($str$db$strategy) {
    global 
$PHP_SELF;
    
$query = new DictQuery();
    
$query->define($str$db);
    
$n $query->get("numres");
    
$res $query->get("result");
    
$out "<b>Found ".count($res);
    
$out .= (count($res)==1) ? " hit" " hits";
    
$out .= "</b> - <i>Term: ".$str.", Database: ".$db.", Strategy: ".$strategy;
    
$out .= "</i><br>\n<dl>\n";
    for (
$i=0$i<count($res); $i++) {
        
$entry $res[$i];
        
$out .= "<dt>[".($i 1)."] : ".$entry["dbname"]."</dt>\n";
        
$out .= "<dd><pre>".mkLinks($entry["definition"], $db)."</pre></dd>\n";
    }
    
$out .= "</dl>";
    return 
$out;
}

if (
$query_term){
    
$out doQuery($query_term$database$strategy);
    echo 
$out."\n<hr>\n";
}
?>
Last accessed:
<?php
$end 
time();
echo 
date("Y/m/d H:i:s",time());
echo 
" [Total processing time: ".($end $start)." seconds]"
?>
</body>
</html>


::::::::::::::
extendedsearch.php
::::::::::::::
<!--
  Extended example that uses pkg.dict.org
  (c) 2000, Jesus M. Castagnetto <jmcastagnetto@zkey.com>
  License: GPL, see www.gnu.org/copyleft/gpl.txt

  Changes:
  2000/06/04 - Added the correct link to the GPL license
  2000/06/05 - Fixes to bugs reported by Davor Cengija <davor@croart.com>
               (1) Links that look like {{UNIX}} were not being stripped
                   correctly
               (2) Crossreference terms sometimes spanned a line break
                   which looked real ugly/bad
               Thanks to Davor for reporting the bugs in this example
  2000/06/05 - Found another bug, some crossreferences are to URLs and
               not terms in the dictionaries, the makeLink() function has
               been fixed to account for that.
  2000/06/06 - Check for duplicates when creating the links, and modified
               the external URLs regex to recognize more protocols, as well
               as eliminate crossrefences to RFC's
  2000/06/07 - Stoyan Jekov <jekov@cig.nml.mot.com> has sharp eye, and 
               noticed that I forgot to close my HTML FORM element
-->

<?php 

    $start
=time(); 
    include (
"./pkg.dict.php");
?>
<html>
<head>
<title>Extended autogenerated form to query dict.org</title>
</head>
<body>
Search the dict.org server
<p>
<form action="<?php echo $PHP_SELF ?>" method="post">
<input type="text" name="query_term" size="60" 
<?php
if ($query_term)
    echo 
"value=\"".$query_term."\"";
?>
><BR>
<?php

/* 
 * I will autogenerate the form in the code below
 * You might want to make a static form to avoid 
 * the overhead of the on-the-fly HTML code generation
 * and the query to the DICT server
 */

// functions to make the form

function mkSelect($name$nopt$options$default) {
    
$out "<select name=\"".$name."\">\n";
    list(
$defkey,$defval) = each($default);
    
$out .= "<option value=\"".$defkey."\" selected>".$defval."</option>\n";
    while (list(
$key,$val) = each($options)) {
        if (
$key != $defkey)
            
$out .= "<option value=\"".$key."\">".$val."</option>\n";
    }
    
$out .= "</select>\n";
    return 
$out;
}

function 
printForm($s) {
    
$ndbs $s->get_info("num_dbs");
    
$dbs $s->get_info("databases");
    
$nstrats $s->get_info("num_strats");
    
$strategies $s->get_info("strategies");
    
$out "<BR>Use database:<BR>\n";
    
$out .= mkSelect("database"$ndbs$dbs, array("*"=>"Any"));
    
$out .= "<BR>Type of Search:<BR>\n";
    
$out .= mkSelect("strategy"$ndbs$strategies, array("exact"=>"Exact Search"));
    echo 
$out;
}

// get non-extended info from the server
$server = new DictServerInfo();

// print the form

printForm($server);

?>
<br>
<input type="submit" name="submit" value=" Search "> 
<input type="reset" name="reset" value=" Clear form input ">
</form>
<hr>
<?php

/*
 * If there was a query, this part will show the result
 */

// check if element is in the array

function inArray($element$arr) {
    
// figure out version
    
list($major$minor$release) = explode("."phpversion());
    if ((
$major == && $relesase >= 12) || $major == 4) {
        return 
in_array($element$arr);
    } else {
        
// assumes that we want to compare element value
        
while (list($key$val) = each($arr)) {
            if (
$val == $element)
                return 
true;
        }
        return 
false;
    }
}

// remove duplicates from array
// and eliminate the patterns in $nolinks

function cleanArray($arr) {
    
$nolinks "rfc:";
    
$out = array();
    for (
$i=0$i<count($arr); $i++)
        if (!
inArray($arr[$i], $out) && !ereg($nolinks$arr[$i])) 
            
$out[] = $arr[$i];
    return 
$out;
}


//make the links to other words in the description

function mkLinks($str$db) {
    global 
$PHP_SELF;
    
    
// modified the regexes to fix the bug reported by <davor@croart.com>
    
$patt "\{+([^{}]+)\}+";
    
$regex "<b>\\1</b>";
    
$out ereg_replace($patt$regex$str);
    
$patt "/\{+([^{}]+)\}+/";
    
preg_match_all($patt$str, &$reg);
    
$link $reg[1];
    
// clean up array
    
$link cleanArray($link);
    if (
count($link) > 0)
        
$out .= "<i>See also:</i>\n";
    
    for (
$i=0$i<count($link); $i++) {
        
// added the line below to fix a second bug reported by <davor@croart.com>
        
$link[$i] = ereg_replace("[[:space:]]+"" "$link[$i]);
        
// observed myself another bug with references to URLs - JMC
        // check if it is a HTTP URL or a crossrefence
        
$protocols "(http|https|ftp|telnet|gopher)://|(mailto|news):";
        if (
ereg($protocols$link[$i])) {
            
// parse the link and mark it using <>
            
$prot1 "^((mailto|news):.+)$";
            
$prot2 "(http|https|ftp|telnet|gopher)://";
            
$prot2 "^(.*) *\((".$prot2.".+)\)$";
            if (
ereg($prot1$link[$i], &$regurl)) {
                list (
$tmp$url) = $regurl;
                
$desc $url;
            } elseif (
ereg($prot2$link[$i], &$regurl)) {
                list (
$tmp$desc$url) = $regurl;
                if (
$desc == "")
                    
$desc $url;
            }
            
$out .= "&lt;<a href=\"".chop($url)."\" target=\"_blank\">";
            
$out .= chop($desc)."</a>&gt; ";
        } else {
            
$out .= "[<a href=\"".$PHP_SELF."?query_term=";
            
$out .= urlencode($link[$i])."&database=".urlencode($db);
            
$out .= "&strategy=exact\">".$link[$i]."</a>] ";
            if ((
$i 5) == && $i 0)
                
$out .= "\n";
        }
    }
    
$out .= "\n";
    return 
$out;
}


// Perform a query to the server

function doQuery($str$db$strategy) {
    global 
$PHP_SELF;
    
$query = new DictQuery();
    if (
$strategy == "exact") {
        
$query->define($str$db);
    } else {
        
$query->match($str$strategy$db);
    }
    
$n $query->get("numres");
    
$res $query->get("result");
    
$out "<b>Found ".count($res);
    
$out .= (count($res)==1) ? " hit" " hits";
    
$out .= "</b> - <i>Term: ".$str.", Database: ".$db.", Strategy: ".$strategy;
    
$out .= "</i><br>\n<dl>\n";
    for (
$i=0$i<count($res); $i++) {
        
$entry $res[$i];
        if (
$strategy == "exact") {        
            
$out .= "<dt>[".($i 1)."] : ".$entry["dbname"]."</dt>\n";
            
$out .= "<dd><pre>".mkLinks($entry["definition"], $db)."</pre></dd>\n";
        } else {
            
$match explode(" ",chop($entry));
            
$match_term str_replace("\""""$match[1]);
            
$out .= "<dt>[". ($i ) . "] : ";
            
$out .= "<A HREF=\"".$PHP_SELF."?query_term=".urlencode($match_term);
            
$out .= "&database=".urlencode($db);
            
$out .= "&strategy=exact\">";
            
$out .= $match_term."</a></dt>\n";
            
$out .= "<dd> Database: ".$match[0]."</dd>";
        }
    }
    
$out .= "</dl>";
    return 
$out;
}

// if there was a query ...
if ($query_term){
    
$out doQuery($query_term$database$strategy);
    echo 
$out."\n<hr>\n";
}
?>
Last accessed:
<?php
$end 
time();
echo 
date("Y/m/d H:i:s",time());
echo 
" [Total processing time: ".($end $start)." seconds]"
?>
</body>
</html>

Comments or questions?
PX is running PHP 5.2.17
Thanks to Miranda Productions for hosting and bandwidth.
Use of any code from PX is at your own risk.