[system] / trunk / wwmoodle / wwquestion / locallib.php Repository:
ViewVC logotype

View of /trunk/wwmoodle/wwquestion/locallib.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5486 - (download) (as text) (annotate)
Tue Sep 11 20:10:03 2007 UTC (12 years, 5 months ago) by mleventi
File size: 29677 byte(s)
Version .4 Partial Answer Questions now supported

    1 <?php
    2 
    3 require_once("$CFG->dirroot/question/type/webwork/config.php");
    4 require_once("$CFG->dirroot/question/type/webwork/questiontype.php");
    5 require_once("$CFG->libdir/soap/nusoap.php");
    6 require_once("$CFG->libdir/filelib.php");
    7 require_once("$CFG->libdir/setuplib.php");
    8 require_once("$CFG->libdir/formslib.php");
    9 
   10 /**
   11 * @desc Gets derived questions from a webworkquestion record object by calling the SOAP server.
   12 * @param $webworkquestion The record to create from
   13 * @return $response The recordset from the WeBWorK Question Server.
   14 */
   15 function webwork_get_derivations($wwquestion) {
   16     //parameters needed from the webworkquestion object
   17     $code = $wwquestion->code;
   18     $seed = $wwquestion->seed;
   19     $trials = $wwquestion->trials;
   20     $files = $wwquestion->files;
   21 
   22     //problem to be generated
   23     $problem = array();
   24     $problem['code'] = $code;
   25     $problem['seed'] = $seed;
   26     $problem['files'] = $files;
   27 
   28     //requested # times for generation
   29     $request = array();
   30     $request['trials'] = $trials;
   31     $request['problem'] = $problem;
   32 
   33     //SOAP CALL
   34     $params = array($request);
   35     $client = new webwork_client();
   36     $response = $client->handler('generateProblem',$params);
   37     return $response;
   38 }
   39 
   40 /**
   41 * @desc Deletes a wwquestion directory
   42 * @param integer $wwquestionid The id of the wwquestion.
   43 * @return bool true
   44 */
   45 function webwork_delete_wwquestion_dir($wwquestionid) {
   46     global $CFG;
   47     fulldelete($CFG->dataroot . WWQUESTION_ROOTDIR . '/' . $wwquestionid . '/');
   48     return true;
   49 }
   50 
   51 /**
   52 * @desc Deletes derivation db records for a wwquestion
   53 * @param integer $wwquestionid The id of the wwquestion.
   54 * @return bool true
   55 */
   56 function webwork_delete_derivations_db($wwquestionid) {
   57     delete_records("question_webwork_derived", "question_webwork", $wwquestionid);
   58     return true;
   59 }
   60 
   61 /**
   62 * @desc Deletes all derivations of a wwquestion.
   63 * @param integer $wwquestionid The id of the wwquestion.
   64 * @return bool true
   65 */
   66 function webwork_delete_derivations($wwquestionid) {
   67     $recordset = get_records('question_webwork_derived','question_webwork',$wwquestionid);
   68     foreach($recordset as $record) {
   69         $derivationid = $record->id;
   70         webwork_delete_derivation_dir($wwquestionid,$derivationid);
   71     }
   72     delete_records("question_webwork_derived", "question_webwork", $wwquestionid);
   73     return true;
   74 }
   75 
   76 /**
   77 * @desc Deletes a derivation db record
   78 * @param integer $derivationid The derivation id.
   79 * @return bool true
   80 */
   81 function webwork_delete_derivation_db($derivationid) {
   82     delete_records('question_webwork_derived','id',$derivationid);
   83     return true;
   84 }
   85 
   86 /**
   87 * @desc Deletes a derivation's directory.
   88 * @param integer $wwquestionid The wwquestion id.
   89 * @param integer $derivationid The derivation's id.
   90 * @return bool true.
   91 */
   92 function webwork_delete_derivation_dir($wwquestionid, $derivationid) {
   93     global $CFG;
   94     $dirpath = $CFG->dataroot . webwork_get_derivation_path($wwquestionid, $derivationid);
   95     fulldelete($dirpath);
   96     return true;
   97 }
   98 
   99 /**
  100 * @desc Parses an answer's preview html to change the image src path. Copies images into user data on a derivation.
  101 * @param string $html The preview answer html.
  102 * @param string $replacer The new src url path.
  103 * @param string $copyto The place to copy the image to.
  104 */
  105 function webwork_parse_change_ans($html,$replacer,$copyto) {
  106     $parsedhtml = "";
  107     $parser = new HtmlParser($html);
  108     while($parser->parse()) {
  109         if($parser->iNodeType == NODE_TYPE_ELEMENT) {
  110             $name = $parser->iNodeName;
  111             if(strcasecmp($name,'img') == 0) {
  112                 $src = $parser->iNodeAttributes['src'];
  113                 //an equation, need to copy
  114                 $srccapture = strrchr($src,'/');
  115                 webwork_copy_file($src,$copyto . $srccapture);
  116                 $parser->iNodeAttributes['src'] = $replacer . $srccapture;
  117             }
  118         }
  119         $parsedhtml .= $parser->printTag();
  120     }
  121     return $parsedhtml;
  122 }
  123 
  124 /**
  125 * @desc Parses a derivation record. This changes all paths of external entities. Copies all external entities to local data folder.
  126 * @param object $derivation The derivation object.
  127 * @return bool true.
  128 */
  129 function webwork_parse_change_derivation(&$derivation) {
  130     //webwork question id
  131     $wwquestionid = $derivation->question_webwork;
  132     //assign paths
  133     $wwquestioncopyto = webwork_get_wwquestion_path_full($wwquestionid);
  134     $wwquestionreplacer = webwork_get_wwquestion_url($wwquestionid);
  135     $derivationcopyto = webwork_get_derivation_path_full($wwquestionid,$derivation->id);
  136     $derivationreplacer = webwork_get_derivation_url($wwquestionid,$derivation->id);
  137     $parsedhtml = "";
  138     $html = base64_decode($derivation->html);
  139     $parser = new HtmlParser($html);
  140     while($parser->parse()) {
  141         if($parser->iNodeType == NODE_TYPE_ELEMENT) {
  142             $nodename = $parser->iNodeName;
  143             if(strcasecmp($nodename,'img') == 0) {
  144                 //image, path fixing
  145                 if(isset($parser->iNodeAttributes['src'])) {
  146                     $src = $parser->iNodeAttributes['src'];
  147                     if(!(strstr($src,'/tmp/') == false)) {
  148                         //image produced by the server
  149                         $srccapture = strrchr($src,'/');
  150                         webwork_copy_file($src,$derivationcopyto . $srccapture);
  151                         $parser->iNodeAttributes['src'] = $derivationreplacer . $srccapture;
  152                     }
  153                 }
  154             } else if(strcasecmp($nodename,'a') == 0) {
  155                 //hyperlink, path fixing
  156                 if(isset($parser->iNodeAttributes['href'])) {
  157                     $href = $parser->iNodeAttributes['href'];
  158                     $hrefcapture = strrchr($href,'/');
  159                     webwork_copy_file($href,$derivationcopyto . $hrefcapture);
  160                     $parser->iNodeAttributes['href'] = $derivationreplacer . $hrefcapture;
  161                 }
  162             }
  163         }
  164         $parsedhtml .= $parser->printTag();
  165     }
  166     $derivation->html = base64_encode($parsedhtml);
  167     return true;
  168 }
  169 
  170 function webwork_codecheck($data,$wwquestionid,$questioncopy) {
  171     //codechecklevel
  172     $codechecklevel = $data['codecheck'];
  173 
  174     //here we construct a temp question object
  175     $question = new stdClass;
  176     $question->code = base64_encode(stripslashes($data['code']));
  177     $question->seed = $data['seed'];
  178     $question->trials = $data['trials'];
  179 
  180     //handle the new question from old functionality
  181     if($questioncopy) {
  182         webwork_make_tmp_dir();
  183         $path = webwork_get_wwquestion_path_full($wwquestionid);
  184         $filelist = list_directories_and_files($path);
  185         //copy everthing but derivations folder into tmp folder
  186         foreach($filelist as $file) {
  187             if($file != 'derivations') {
  188                 webwork_copy_file("$path/$file",webwork_get_tmp_path_full() . '/' . $file);
  189             }
  190         }
  191     }
  192 
  193 
  194     //should we look in tmp or in wwquestion dir to find stuff
  195     if(!isset($wwquestionid)) {
  196         $path = webwork_get_tmp_path_full();
  197         $urlpath = webwork_get_filehandler_path() . '/' . webwork_get_tmp_path() . '/';
  198     } else {
  199         $path = webwork_get_wwquestion_path_full($this->question->webworkid);
  200         $urlpath = webwork_get_filehandler_path() . '/' . webwork_get_wwquestion_path($this->question->webworkid) . '/';
  201     }
  202     $filelist = list_directories_and_files($path);
  203     $filearray = array();
  204 
  205     //files that need to be pushed
  206     foreach($filelist as $file) {
  207         if(!is_dir($path . '/' . $file)) {
  208             $encode = base64_encode($urlpath . '/' . $file);
  209             array_push($filearray,$encode);
  210         }
  211     }
  212 
  213     $question->files = $filearray;
  214 
  215     //one call to the server will return response for this code and keep it static in the function
  216     $results = webwork_get_derivations($question);
  217 
  218     //filter errors and warnings
  219     $errorresults = array();
  220     $noerrorresults = array();
  221     $warningresults = array();
  222     $goodresults = array();
  223     foreach($results as $record) {
  224         if((isset($record['errors'])) && ($record['errors'] != '') && ($record['errors'] != null)) {
  225             array_push($errorresults,$record);
  226         } else {
  227             array_push($noerrorresults,$record);
  228         }
  229     }
  230     foreach($noerrorresults as $record) {
  231         if((isset($record['warnings'])) && ($record['warnings'] != '') && ($record['warnings'] != null)) {
  232             array_push($warningresults,$record);
  233         } else {
  234             array_push($goodresults,$record);
  235         }
  236     }
  237 
  238     switch($codechecklevel) {
  239         //No code check
  240         case 0:
  241             webwork_qtype::_derivations($results);
  242             return true;
  243             break;
  244         //reject seeds with errors
  245         case 1:
  246             if(count($noerrorresults) > 0) {
  247                 webwork_qtype::_derivations($noerrorresults);
  248                 return true;
  249             }
  250             break;
  251         //reject if errors
  252         case 2:
  253             if(count($noerrorresults) == count($results)) {
  254                 webwork_qtype::_derivations($results);
  255                 return true;
  256             }
  257             break;
  258         //reject seeds with errors or warnings
  259         case 3:
  260             if(count($goodresults) > 0) {
  261                 webwork_qtype::_derivations($goodresults);
  262                 return true;
  263             }
  264             break;
  265         //reject if errors or warnings
  266         case 4:
  267             if(count($goodresults) == count($results)) {
  268                 webwork_qtype::_derivations($results);
  269                 return true;
  270             }
  271             break;
  272     }
  273 
  274     $errormsgs = array();
  275     $warningmsgs = array();
  276     //at this point we are going to be invalid
  277     //this correlates seeds with certain error messages for better output
  278     //ERRORS
  279     foreach($errorresults as $record) {
  280         $found = 0;
  281         $candidate = $record['errors'] . "<br>";
  282         $candidateseed = $record['seed'];
  283         for($i=0;$i<count($errormsgs);$i++) {
  284             if($candidate == $errormsgs[$i]['errors']) {
  285                 $found = 1;
  286                 $errormsgs[$i]['seeds'][] = $candidateseed;
  287             }
  288         }
  289         if($found == 0) {
  290             //new error message
  291             $msg = array();
  292             $msg['errors'] = $candidate;
  293             $msg['seeds'] = array();
  294             $msg['seeds'][] = $candidateseed;
  295             $errormsgs[] = $msg;
  296         }
  297     }
  298     //WARNINGS
  299     foreach($warningresults as $record) {
  300         $found = 0;
  301         $candidate = $record['warnings'] . "<br>";
  302         $candidateseed = $record['seed'];
  303         for($i=0;$i<count($warningmsgs);$i++) {
  304             if($candidate == $warningmsgs[$i]['errors']) {
  305                 $found = 1;
  306                 $warningmsgs[$i]['seeds'][] = $candidateseed;
  307             }
  308         }
  309         if($found == 0) {
  310             //new error message
  311             $msg = array();
  312             $msg['warnings'] = $candidate;
  313             $msg['seeds'] = array();
  314             $msg['seeds'][] = $candidateseed;
  315             $warningmsgs[] = $msg;
  316         }
  317 
  318     }
  319     $output = "Errors in PG Code on: " . count($errorresults) . " out of " . count($results) . " seeds tried:<br>";
  320     //construct error statement
  321     $counter = 1;
  322     foreach($errormsgs as $msg) {
  323         $output .= "$counter) ";
  324         $output .= "Seeds (";
  325         foreach ($msg['seeds'] as $seed) {
  326             $output .= $seed . " ";
  327         }
  328         $output .= ") gave Errors:" . $msg['errors'] . "<br><br>";
  329         $counter++;
  330     }
  331     $output .= "Warnings in PG Code on: " . count($warningresults) . " out of " . count($results) . " seeds tried:<br>";
  332     $counter = 1;
  333     foreach($warningmsgs as $msg) {
  334         $output .= "$counter) ";
  335         $output .= "Seeds (";
  336         foreach ($msg['seeds'] as $seed) {
  337             $output .= $seed . " ";
  338         }
  339         $output .= ") gave Warnings:" . $msg['warnings'] . "<br><br>";
  340         $counter++;
  341     }
  342     $returner =array();
  343     $returner['code'] = $output;
  344     return $returner;
  345 }
  346 
  347 //////////////////////////////////////////////////////////////////////////////////
  348 //WEBWORK FILE RESOLVERS
  349 //////////////////////////////////////////////////////////////////////////////////
  350 
  351 //These functions return paths to different resources in the filesystem
  352 
  353 
  354     function webwork_get_derivation_user_url($wwquestionid,$derivationid,$userid) {
  355         return webwork_get_filehandler_path() . webwork_get_derivation_user_path($wwquestionid,$derivationid,$userid);
  356     }
  357 
  358     function webwork_get_derivation_user_path_full($wwquestionid,$derivationid,$userid) {
  359         global $CFG;
  360         return $CFG->dataroot . webwork_get_derivation_user_path($wwquestionid,$derivationid,$userid);
  361     }
  362 
  363     function webwork_get_derivation_user_path($wwquestionid,$derivationid,$userid) {
  364         return webwork_get_derivation_path($wwquestionid,$derivationid) . '/users/' . $userid;
  365     }
  366 
  367     function webwork_get_derivation_url($wwquestionid, $derivationid) {
  368         return webwork_get_filehandler_path() . webwork_get_derivation_path($wwquestionid,$derivationid);
  369     }
  370 
  371     function webwork_get_derivation_path_full($wwquestionid, $derivationid) {
  372         global $CFG;
  373         return $CFG->dataroot . webwork_get_derivation_path($wwquestionid, $derivationid);
  374     }
  375 
  376     function webwork_get_derivation_path($wwquestionid, $derivationid) {
  377         return WWQUESTION_ROOTDIR . '/' . $wwquestionid . '/derivations/' . $derivationid;
  378     }
  379 
  380     function webwork_get_wwquestion_url($wwquestionid) {
  381         return webwork_get_filehandler_path() . webwork_get_wwquestion_path($wwquestionid);
  382     }
  383 
  384     function webwork_get_wwquestion_path_full($wwquestionid) {
  385         global $CFG;
  386         return $CFG->dataroot . webwork_get_wwquestion_path($wwquestionid);
  387     }
  388 
  389     function webwork_get_wwquestion_path($wwquestionid) {
  390         return WWQUESTION_ROOTDIR . '/' . $wwquestionid;
  391     }
  392 
  393     function webwork_get_filemanager_url($wwquestionid) {
  394         global $CFG;
  395         return $CFG->wwwroot . "/question/type/webwork/files.php?id=".SITEID.'&qid='.$wwquestionid;
  396     }
  397 
  398     function webwork_get_tmp_path_full() {
  399         global $CFG;
  400         return $CFG->dataroot . webwork_get_tmp_path();
  401     }
  402 
  403     function webwork_get_tmp_path() {
  404         global $USER;
  405         return WWQUESTION_ROOTDIR . '/tmp' . $USER->id;
  406     }
  407 
  408     function webwork_get_filehandler_path() {
  409         global $CFG;
  410         return $CFG->wwwroot . "/question/type/webwork/file.php";
  411     }
  412 
  413     function webwork_make_tmp_dir() {
  414         return make_upload_directory(webwork_get_tmp_path());
  415     }
  416 
  417     /**
  418     * @desc Copies a file if needed from $srcpath to $dstpath.
  419     * @param $srcpath string The source.
  420     * @param $dstpath string The destination.
  421     * @return bool true
  422     */
  423     function webwork_copy_file($srcpath,$dstpath) {
  424         if(!file_exists($dstpath)) {
  425             $err = copy($srcpath,$dstpath);
  426             if($err == false) {
  427                 print_error("Copy Failed for: '".$srcpath."' to '".$dstpath."'");
  428                 return false;
  429             }
  430         }
  431         return true;
  432     }
  433 
  434     /**
  435     * @desc Makes a directory for a derivation.
  436     * @param $wwquestionid integer The id of a webwork_question.
  437     * @param $derivationid integer The id of a derivation.
  438     * @return bool true.
  439     */
  440     function webwork_make_derivation_dir($wwquestionid,$derivationid) {
  441         return make_upload_directory(webwork_get_derivation_path($wwquestionid,$derivationid));
  442     }
  443 
  444     /**
  445     * @desc Makes a directory for a derivation for a specific user.
  446     * @param $wwquestionid integer The id of a webwork_question.
  447     * @param $derivationid integer The id of a derivation.
  448     * @param $userid integer The id of a user.
  449     * @return bool true.
  450     */
  451     function webwork_make_derivation_user_dir($wwquestionid,$derivationid,$userid) {
  452         return make_upload_directory(webwork_get_derivation_user_path($wwquestionid,$derivationid,$userid));
  453     }
  454 
  455     /**
  456     * @desc Makes a directory for a wwquestion.
  457     * @param integer $wwquestionid The wwquestion id.
  458     * @return bool directory creation.
  459     */
  460     function webwork_make_wwquestion_dir($wwquestionid) {
  461         return make_upload_directory(webwork_get_wwquestion_path($wwquestionid));
  462     }
  463 
  464 //////////////////////////////////////////////////////////////////////////////////
  465 //WEBWORK CLIENT
  466 //////////////////////////////////////////////////////////////////////////////////
  467 
  468 /**
  469 * @desc Singleton class that contains function for communication to the WeBWorK Question Server.
  470 */
  471 class webwork_client {
  472     var $client;
  473 
  474     /**
  475     * @desc Constructs a singleton problemserver_client. Uses nusoap libraries php(4 & 5)
  476     */
  477     function webwork_client() {
  478         //static associative array containing the real objects, key is classname
  479         static $instances=array();
  480         //get classname
  481         $class = get_class($this);
  482         if (!array_key_exists($class, $instances)) {
  483             //does not yet exist, save in array
  484             $this->client = new soap_client(WWQUESTION_WSDL,'wsdl',false,false,false,false,0,WWQUESTION_RESPONSE_TIMEOUT);
  485             $err = $this->client->getError();
  486             if ($err) {
  487                 print_error($err . " " . get_string('error_client_construction','qtype_webwork'));
  488             }
  489             $instances[$class] = $this;
  490         }
  491         foreach (get_class_vars($class) as $var => $value) {
  492             $this->$var =& $instances[$class]->$var;
  493         }
  494     }
  495 
  496     /**
  497     * @desc Calls a SOAP function on the server.
  498     * @param string $functioncall The function to call
  499     * @param array $params The parameters to the function.
  500     * @return Result of the soap function.
  501     */
  502     function handler($functioncall,$params=array()) {
  503         if(!is_array($params)) {
  504             $params = array($params);
  505         }
  506         $result = $this->client->call($functioncall,$params);
  507         //$result = call_user_func_array(array(&$this->client,$functioncall),$params);
  508         if($err = $this->client->getError()) {
  509             //print_error(get_string("rpc_fault","wwassignment') . " " . $functioncall. " ". $err);
  510             print_error($err . " " . get_string('error_client_call','qtype_webwork'));
  511         }
  512         return $result;
  513     }
  514 }
  515 
  516 //////////////////////////////////////////////////////////////////////////////////
  517 //HTML PARSER
  518 //////////////////////////////////////////////////////////////////////////////////
  519 
  520 /*
  521  * Copyright (c) 2003 Jose Solorzano.  All rights reserved.
  522  * Redistribution of source must retain this copyright notice.
  523  *
  524  * Jose Solorzano (http://jexpert.us) is a software consultant.
  525  *
  526  * Contributions by:
  527  * - Leo West (performance improvements)
  528  */
  529 
  530 define ("NODE_TYPE_START",0);
  531 define ("NODE_TYPE_ELEMENT",1);
  532 define ("NODE_TYPE_ENDELEMENT",2);
  533 define ("NODE_TYPE_TEXT",3);
  534 define ("NODE_TYPE_COMMENT",4);
  535 define ("NODE_TYPE_DONE",5);
  536 
  537 /**
  538  * Class HtmlParser.
  539  * To use, create an instance of the class passing
  540  * HTML text. Then invoke parse() until it's false.
  541  * When parse() returns true, $iNodeType, $iNodeName
  542  * $iNodeValue and $iNodeAttributes are updated.
  543  *
  544  * To create an HtmlParser instance you may also
  545  * use convenience functions HtmlParser_ForFile
  546  * and HtmlParser_ForURL.
  547  */
  548 class HtmlParser {
  549 
  550     /**
  551      * Field iNodeType.
  552      * May be one of the NODE_TYPE_* constants above.
  553      */
  554     var $iNodeType;
  555 
  556     /**
  557      * Field iNodeName.
  558      * For elements, it's the name of the element.
  559      */
  560     var $iNodeName = "";
  561 
  562     /**
  563      * Field iNodeValue.
  564      * For text nodes, it's the text.
  565      */
  566     var $iNodeValue = "";
  567 
  568     /**
  569      * Field iNodeAttributes.
  570      * A string-indexed array containing attribute values
  571      * of the current node. Indexes are always lowercase.
  572      */
  573     var $iNodeAttributes;
  574 
  575     // The following fields should be
  576     // considered private:
  577 
  578     var $iHtmlText;
  579     var $iHtmlTextLength;
  580     var $iHtmlTextIndex = 0;
  581     var $iHtmlCurrentChar;
  582     var $BOE_ARRAY;
  583     var $B_ARRAY;
  584     var $BOS_ARRAY;
  585 
  586     /**
  587      * Constructor.
  588      * Constructs an HtmlParser instance with
  589      * the HTML text given.
  590      */
  591     function HtmlParser ($aHtmlText) {
  592         $this->iHtmlText = $aHtmlText;
  593         $this->iHtmlTextLength = strlen($aHtmlText);
  594         $this->iNodeAttributes = array();
  595         $this->setTextIndex (0);
  596 
  597         $this->BOE_ARRAY = array (" ", "\t", "\r", "\n", "=" );
  598         $this->B_ARRAY = array (" ", "\t", "\r", "\n" );
  599         $this->BOS_ARRAY = array (" ", "\t", "\r", "\n", "/" );
  600     }
  601 
  602     /**
  603      * Method parse.
  604      * Parses the next node. Returns false only if
  605      * the end of the HTML text has been reached.
  606      * Updates values of iNode* fields.
  607      */
  608     function parse() {
  609         $text = $this->skipToElement();
  610         if ($text != "") {
  611             $this->iNodeType = NODE_TYPE_TEXT;
  612             $this->iNodeName = "Text";
  613             $this->iNodeValue = $text;
  614             return true;
  615         }
  616         return $this->readTag();
  617     }
  618 
  619     function printTag() {
  620         if($this->iNodeType == NODE_TYPE_ELEMENT) {
  621             $temp = "<";
  622             $temp .= $this->iNodeName;
  623             if(isset($this->iNodeAttributes['selected'])) {
  624                 $temp .= " selected";
  625             }
  626             foreach($this->iNodeAttributes as $key => $value) {
  627                 if($key == 'selected') {
  628                 } else {
  629                     $temp .= " " . $key . "=" . '"' . $value . '"';
  630                 }
  631             }
  632             $temp .= ">";
  633         } else if($this->iNodeType == NODE_TYPE_ENDELEMENT) {
  634             $temp = "</" . $this->iNodeName . ">";
  635         } else if($this->iNodeType == NODE_TYPE_TEXT) {
  636             $temp = $this->iNodeValue;
  637         } else {
  638         }
  639         return $temp;
  640 
  641 
  642     }
  643 
  644     function clearAttributes() {
  645         $this->iNodeAttributes = array();
  646     }
  647 
  648     function readTag() {
  649         if ($this->iCurrentChar != "<") {
  650             $this->iNodeType = NODE_TYPE_DONE;
  651             return false;
  652         }
  653         $this->clearAttributes();
  654         $this->skipMaxInTag ("<", 1);
  655         if ($this->iCurrentChar == '/') {
  656             $this->moveNext();
  657             $name = $this->skipToBlanksInTag();
  658             $this->iNodeType = NODE_TYPE_ENDELEMENT;
  659             $this->iNodeName = $name;
  660             $this->iNodeValue = "";
  661             $this->skipEndOfTag();
  662             return true;
  663         }
  664         $name = $this->skipToBlanksOrSlashInTag();
  665         if (!$this->isValidTagIdentifier ($name)) {
  666                 $comment = false;
  667                 if (strpos($name, "!--") === 0) {
  668                     $ppos = strpos($name, "--", 3);
  669                     if (strpos($name, "--", 3) === (strlen($name) - 2)) {
  670                         $this->iNodeType = NODE_TYPE_COMMENT;
  671                         $this->iNodeName = "Comment";
  672                         $this->iNodeValue = "<" . $name . ">";
  673                         $comment = true;
  674                     }
  675                     else {
  676                         $rest = $this->skipToStringInTag ("-->");
  677                         if ($rest != "") {
  678                             $this->iNodeType = NODE_TYPE_COMMENT;
  679                             $this->iNodeName = "Comment";
  680                             $this->iNodeValue = "<" . $name . $rest;
  681                             $comment = true;
  682                             // Already skipped end of tag
  683                             return true;
  684                         }
  685                     }
  686                 }
  687                 if (!$comment) {
  688                     $this->iNodeType = NODE_TYPE_TEXT;
  689                     $this->iNodeName = "Text";
  690                     $this->iNodeValue = "<" . $name;
  691                     return true;
  692                 }
  693         }
  694         else {
  695                 $this->iNodeType = NODE_TYPE_ELEMENT;
  696                 $this->iNodeValue = "";
  697                 $this->iNodeName = $name;
  698                 while ($this->skipBlanksInTag()) {
  699                     $attrName = $this->skipToBlanksOrEqualsInTag();
  700                     if ($attrName != "" && $attrName != "/") {
  701                         $this->skipBlanksInTag();
  702                         if ($this->iCurrentChar == "=") {
  703                             $this->skipEqualsInTag();
  704                             $this->skipBlanksInTag();
  705                             $value = $this->readValueInTag();
  706                             $this->iNodeAttributes[strtolower($attrName)] = $value;
  707                         }
  708                         else {
  709                             $this->iNodeAttributes[strtolower($attrName)] = "";
  710                         }
  711                     }
  712                 }
  713         }
  714         $this->skipEndOfTag();
  715         return true;
  716     }
  717 
  718     function isValidTagIdentifier ($name) {
  719         return ereg ("^[A-Za-z0-9_\\-]+$", $name);
  720     }
  721 
  722     function skipBlanksInTag() {
  723         return "" != ($this->skipInTag ($this->B_ARRAY));
  724     }
  725 
  726     function skipToBlanksOrEqualsInTag() {
  727         return $this->skipToInTag ($this->BOE_ARRAY);
  728     }
  729 
  730     function skipToBlanksInTag() {
  731         return $this->skipToInTag ($this->B_ARRAY);
  732     }
  733 
  734     function skipToBlanksOrSlashInTag() {
  735         return $this->skipToInTag ($this->BOS_ARRAY);
  736     }
  737 
  738     function skipEqualsInTag() {
  739         return $this->skipMaxInTag ("=", 1);
  740     }
  741 
  742     function readValueInTag() {
  743         $ch = $this->iCurrentChar;
  744         $value = "";
  745         if ($ch == "\"") {
  746             $this->skipMaxInTag ("\"", 1);
  747             $value = $this->skipToInTag ("\"");
  748             $this->skipMaxInTag ("\"", 1);
  749         }
  750         else if ($ch == "'") {
  751             $this->skipMaxInTag ("'", 1);
  752             $value = $this->skipToInTag ("'");
  753             $this->skipMaxInTag ("'", 1);
  754         }
  755         else {
  756             $value = $this->skipToBlanksInTag();
  757         }
  758         return $value;
  759     }
  760 
  761     function setTextIndex ($index) {
  762         $this->iHtmlTextIndex = $index;
  763         if ($index >= $this->iHtmlTextLength) {
  764             $this->iCurrentChar = -1;
  765         }
  766         else {
  767             $this->iCurrentChar = $this->iHtmlText{$index};
  768         }
  769     }
  770 
  771     function moveNext() {
  772         if ($this->iHtmlTextIndex < $this->iHtmlTextLength) {
  773             $this->setTextIndex ($this->iHtmlTextIndex + 1);
  774             return true;
  775         }
  776         else {
  777             return false;
  778         }
  779     }
  780 
  781     function skipEndOfTag() {
  782         while (($ch = $this->iCurrentChar) !== -1) {
  783             if ($ch == ">") {
  784                 $this->moveNext();
  785                 return;
  786             }
  787             $this->moveNext();
  788         }
  789     }
  790 
  791     function skipInTag ($chars) {
  792         $sb = "";
  793         while (($ch = $this->iCurrentChar) !== -1) {
  794             if ($ch == ">") {
  795                 return $sb;
  796             } else {
  797                 $match = false;
  798                 for ($idx = 0; $idx < count($chars); $idx++) {
  799                     if ($ch == $chars[$idx]) {
  800                         $match = true;
  801                         break;
  802                     }
  803                 }
  804                 if (!$match) {
  805                     return $sb;
  806                 }
  807                 $sb .= $ch;
  808                 $this->moveNext();
  809             }
  810         }
  811         return $sb;
  812     }
  813 
  814     function skipMaxInTag ($chars, $maxChars) {
  815         $sb = "";
  816         $count = 0;
  817         while (($ch = $this->iCurrentChar) !== -1 && $count++ < $maxChars) {
  818             if ($ch == ">") {
  819                 return $sb;
  820             } else {
  821                 $match = false;
  822                 for ($idx = 0; $idx < count($chars); $idx++) {
  823                     if ($ch == $chars[$idx]) {
  824                         $match = true;
  825                         break;
  826                     }
  827                 }
  828                 if (!$match) {
  829                     return $sb;
  830                 }
  831                 $sb .= $ch;
  832                 $this->moveNext();
  833             }
  834         }
  835         return $sb;
  836     }
  837 
  838     function skipToInTag ($chars) {
  839         $sb = "";
  840         while (($ch = $this->iCurrentChar) !== -1) {
  841             $match = $ch == ">";
  842             if (!$match) {
  843                 for ($idx = 0; $idx < count($chars); $idx++) {
  844                     if ($ch == $chars[$idx]) {
  845                         $match = true;
  846                         break;
  847                     }
  848                 }
  849             }
  850             if ($match) {
  851                 return $sb;
  852             }
  853             $sb .= $ch;
  854             $this->moveNext();
  855         }
  856         return $sb;
  857     }
  858 
  859     function skipToElement() {
  860         $sb = "";
  861         while (($ch = $this->iCurrentChar) !== -1) {
  862             if ($ch == "<") {
  863                 return $sb;
  864             }
  865             $sb .= $ch;
  866             $this->moveNext();
  867         }
  868         return $sb;
  869     }
  870 
  871     /**
  872      * Returns text between current position and $needle,
  873      * inclusive, or "" if not found. The current index is moved to a point
  874      * after the location of $needle, or not moved at all
  875      * if nothing is found.
  876      */
  877     function skipToStringInTag ($needle) {
  878         $pos = strpos ($this->iHtmlText, $needle, $this->iHtmlTextIndex);
  879         if ($pos === false) {
  880             return "";
  881         }
  882         $top = $pos + strlen($needle);
  883         $retvalue = substr ($this->iHtmlText, $this->iHtmlTextIndex, $top - $this->iHtmlTextIndex);
  884         $this->setTextIndex ($top);
  885         return $retvalue;
  886     }
  887 }
  888 
  889 ?>

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9