Source for file XPath.class.php

Documentation is available at XPath.class.php

  1. <?php
  2. /**
  3.  * Php.XPath
  4.  *
  5.  * +======================================================================================================+
  6.  * | A php class for searching an XML document using XPath, and making modifications using a DOM
  7.  * | style API. Does not require the DOM XML PHP library.
  8.  * |
  9.  * +======================================================================================================+
  10.  * | What Is XPath:
  11.  * | --------------
  12.  * | - "What SQL is for a relational database, XPath is for an XML document." -- Sam Blum
  13.  * | - "The primary purpose of XPath is to address parts of an XML document. In support of this
  14.  * |    primary purpose, it also provides basic facilities for manipulting it." -- W3C
  15.  * |
  16.  * | XPath in action and a very nice intro is under:
  17.  * |    http://www.zvon.org/xxl/XPathTutorial/General/examples.html
  18.  * | Specs Can be found under:
  19.  * |    http://www.w3.org/TR/xpath     W3C XPath Recommendation
  20.  * |    http://www.w3.org/TR/xpath20   W3C XPath Recommendation
  21.  * |
  22.  * | NOTE: Most of the XPath-spec has been realized, but not all. Usually this should not be
  23.  * |       problem as the missing part is either rarely used or it's simpler to do with PHP itself.
  24.  * +------------------------------------------------------------------------------------------------------+
  25.  * | Requires PHP version  4.0.5 and up
  26.  * +------------------------------------------------------------------------------------------------------+
  27.  * | Main Active Authors:
  28.  * | --------------------
  29.  * | Nigel Swinson <nigelswinson@users.sourceforge.net>
  30.  * |   Started around 2001-07, saved phpxml from near death and renamed to Php.XPath
  31.  * |   Restructured XPath code to stay in line with XPath spec.
  32.  * | Sam Blum <bs_php@infeer.com>
  33.  * |   Started around 2001-09 1st major restruct (V2.0) and testbench initiator.
  34.  * |   2nd (V3.0) major rewrite in 2002-02
  35.  * | Daniel Allen <bigredlinux@yahoo.com>
  36.  * |   Started around 2001-10 working to make Php.XPath adhere to specs
  37.  * | Main Former Author: Michael P. Mehl <mpm@phpxml.org>
  38.  * |   Inital creator of V 1.0. Stoped activities around 2001-03
  39.  * +------------------------------------------------------------------------------------------------------+
  40.  * | Code Structure:
  41.  * | --------------_
  42.  * | The class is split into 3 main objects. To keep usability easy all 3
  43.  * | objects are in this file (but may be split in 3 file in future).
  44.  * |   +-------------+
  45.  * |   |  XPathBase  | XPathBase holds general and debugging functions.
  46.  * |   +------+------+
  47.  * |          v
  48.  * |   +-------------+ XPathEngine is the implementation of the W3C XPath spec. It contains the
  49.  * |   | XPathEngine | XML-import (parser), -export  and can handle xPathQueries. It's a fully
  50.  * |   +------+------+ functional class but has no functions to modify the XML-document (see following).
  51.  * |          v
  52.  * |   +-------------+
  53.  * |   |    XPath    | XPath extends the functionality with actions to modify the XML-document.
  54.  * |   +-------------+ We tryed to implement a DOM - like interface.
  55.  * +------------------------------------------------------------------------------------------------------+
  56.  * | Usage:
  57.  * | ------
  58.  * | Scroll to the end of this php file and you will find a short sample code to get you started
  59.  * +------------------------------------------------------------------------------------------------------+
  60.  * | Glossary:
  61.  * | ---------
  62.  * | To understand how to use the functions and to pass the right parameters, read following:
  63.  * |
  64.  * | Document: (full node tree, XML-tree)
  65.  * |     After a XML-source has been imported and parsed, it's stored as a tree of nodes sometimes
  66.  * |     refered to as 'document'.
  67.  * |
  68.  * | AbsoluteXPath: (xPath, xPathSet)
  69.  * |     A absolute XPath is a string. It 'points' to *one* node in the XML-document. We use the
  70.  * |     term 'absolute' to emphasise that it is not an xPath-query (see xPathQuery). A valid xPath
  71.  * |     has the form like '/AAA[1]/BBB[2]/CCC[1]'. Usually functions that require a node (see Node)
  72.  * |     will also accept an abs. XPath.
  73.  * |
  74.  * | Node: (node, nodeSet, node-tree)
  75.  * |     Some funtions require or return a node (or a whole node-tree). Nodes are only used with the
  76.  * |     XPath-interface and have an internal structure. Every node in a XML document has a unique
  77.  * |     corresponding abs. xPath. That's why public functions that accept a node, will usually also
  78.  * |     accept a abs. xPath (a string) 'pointing' to an existing node (see absolutXPath).
  79.  * |
  80.  * | XPathQuery: (xquery, query)
  81.  * |     A xPath-query is a string that is matched against the XML-document. The result of the match
  82.  * |     is a xPathSet (vector of xPath's). It's always possible to pass a single absoluteXPath
  83.  * |     instead of a xPath-query. A valid xPathQuery could look like this:
  84.  * |     '//XXX/*[contains(., "foo")]/..' (See the link in 'What Is XPath' to learn more).
  85.  * |
  86.  * |
  87.  * +------------------------------------------------------------------------------------------------------+
  88.  * | Internals:
  89.  * | ----------
  90.  * | - The Node Tree
  91.  * |   -------------
  92.  * | A central role of the package is how the XML-data is stored. The whole data is in a node-tree.
  93.  * | A node can be seen as the equvalent to a tag in the XML soure with some extra info.
  94.  * | For instance the following XML
  95.  * |                        <AAA foo="x">***<BBB/><CCC/>**<BBB/>*</AAA>
  96.  * | Would produce folowing node-tree:
  97.  * |                              'super-root'      <-- $nodeRoot (Very handy)
  98.  * |                                    |
  99.  * |             'depth' 0            AAA[1]        <-- top node. The 'textParts' of this node would be
  100.  * |                                /   |   \                     'textParts' => array('***','','**','*')
  101.  * |             'depth' 1     BBB[1] CCC[1] BBB[2]               (NOTE: Is always size of child nodes+1)
  102.  * | - The Node
  103.  * |   --------
  104.  * | The node itself is an structure desiged mainly to be used in connection with the interface of PHP.XPath.
  105.  * | That means it's possible for functions to return a sub-node-tree that can be used as input of an other
  106.  * | PHP.XPath function.
  107.  * |
  108.  * | The main structure of a node is:
  109.  * |   $node = array(
  110.  * |     'name'        => '',      # The tag name. E.g. In <FOO bar="aaa"/> it would be 'FOO'
  111.  * |     'attributes'  => array(), # The attributes of the tag E.g. In <FOO bar="aaa"/> it would be array('bar'=>'aaa')
  112.  * |     'textParts'   => array(), # Array of text parts surrounding the children E.g. <FOO>aa<A>bb<B/>cc</A>dd</FOO> -> array('aa','bb','cc','dd')
  113.  * |     'childNodes'  => array(), # Array of refences (pointers) to child nodes.
  114.  * |
  115.  * | For optimisation reasions some additional data is stored in the node too:
  116.  * |     'parentNode'  => NULL     # Reference (pointer) to the parent node (or NULL if it's 'super root')
  117.  * |     'depth'       => 0,       # The tag depth (or tree level) starting with the root tag at 0.
  118.  * |     'pos'         => 0,       # Is the zero-based position this node has in the parent's 'childNodes'-list.
  119.  * |     'contextPos'  => 1,       # Is the one-based position this node has by counting the siblings tags (tags with same name)
  120.  * |     'xpath'       => ''       # Is the abs. XPath to this node.
  121.  * |     'generated_id'=> ''       # The id returned for this node by generate-id() (attribute and text nodes not supported)
  122.  * |
  123.  * | - The NodeIndex
  124.  * |   -------------
  125.  * | Every node in the tree has an absolute XPath. E.g '/AAA[1]/BBB[2]' the $nodeIndex is a hash array
  126.  * | to all the nodes in the node-tree. The key used is the absolute XPath (a string).
  127.  * |
  128.  * +------------------------------------------------------------------------------------------------------+
  129.  * | License:
  130.  * | --------
  131.  * | The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
  132.  * | you may not use this file except in compliance with the License. You may obtain a copy of the
  133.  * | License at http://www.mozilla.org/MPL/
  134.  * |
  135.  * | Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY
  136.  * | OF ANY KIND, either express or implied. See the License for the specific language governing
  137.  * | rights and limitations under the License.
  138.  * |
  139.  * | The Original Code is <phpXML/>.
  140.  * |
  141.  * | The Initial Developer of the Original Code is Michael P. Mehl. Portions created by Michael
  142.  * | P. Mehl are Copyright (C) 2001 Michael P. Mehl. All Rights Reserved.
  143.  * |
  144.  * | Contributor(s): N.Swinson / S.Blum / D.Allen
  145.  * |
  146.  * | Alternatively, the contents of this file may be used under the terms of either of the GNU
  147.  * | General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public
  148.  * | License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the
  149.  * | LGPL License are applicable instead of those above.  If you wish to allow use of your version
  150.  * | of this file only under the terms of the GPL or the LGPL License and not to allow others to
  151.  * | use your version of this file under the MPL, indicate your decision by deleting the
  152.  * | provisions above and replace them with the notice and other provisions required by the
  153.  * | GPL or the LGPL License.  If you do not delete the provisions above, a recipient may use
  154.  * | your version of this file under either the MPL, the GPL or the LGPL License.
  155.  * |
  156.  * +======================================================================================================+
  157.  *
  158.  * @author  S.Blum / N.Swinson / D.Allen / (P.Mehl)
  159.  * @link    http://sourceforge.net/projects/phpxpath/
  160.  * @version 3.5
  161.  * @CVS $Id: XPath.class.php,v 1.9 2005/11/16 17:26:05 bigmichi1 Exp $
  162.  */
  163.  
  164. // Include guard, protects file being included twice
  165. $ConstantName 'INCLUDED_'.strtoupper(__FILE__);
  166. if (defined($ConstantName)) return;
  167. define($ConstantName,1TRUE);
  168.  
  169. /************************************************************************************************
  170. * ===============================================================================================
  171. *                               X P a t h B a s e  -  Class                                      
  172. * ===============================================================================================
  173. ************************************************************************************************/
  174. class XPathBase {
  175.   var $_lastError;
  176.   
  177.   // As debugging of the xml parse is spread across several functions, we need to make this a member.
  178.   var $bDebugXmlParse = FALSE;
  179.  
  180.   // do we want to do profiling?
  181.   var $bClassProfiling = FALSE;
  182.  
  183.   // Used to help navigate through the begin/end debug calls
  184.   var $iDebugNextLinkNumber = 1;
  185.   var $aDebugOpenLinks = array();
  186.   var $aDebugFunctions = array(
  187.           //'_evaluatePrimaryExpr',
  188.           //'_evaluateExpr',
  189.           //'_evaluateStep',
  190.           //'_checkPredicates',
  191.           //'_evaluateFunction',
  192.           //'_evaluateOperator',
  193.           //'_evaluatePathExpr',
  194.                );
  195.  
  196.   /**
  197.    * Constructor
  198.    */
  199.   function XPathBase({
  200.     # $this->bDebugXmlParse = TRUE;
  201.     $this->properties['verboseLevel'1;  // 0=silent, 1 and above produce verbose output (an echo to screen). 
  202.     
  203.     if (!isSet($_ENV)) {  // Note: $_ENV introduced in 4.1.0. In earlier versions, use $HTTP_ENV_VARS.
  204.       $_ENV $GLOBALS['HTTP_ENV_VARS'];
  205.     }
  206.     
  207.     // Windows 95/98 do not support file locking. Detecting OS (Operation System) and setting the 
  208.     // properties['OS_supports_flock'] to FALSE if win 95/98 is detected. 
  209.     // This will surpress the file locking error reported from win 98 users when exportToFile() is called.
  210.     // May have to add more OS's to the list in future (Macs?).
  211.     // ### Note that it's only the FAT and NFS file systems that are really a problem.  NTFS and
  212.     // the latest php libs do support flock()
  213.     $_ENV['OS'= isSet($_ENV['OS']$_ENV['OS''Unknown OS';
  214.     switch ($_ENV['OS']
  215.       case 'Windows_95':
  216.       case 'Windows_98':
  217.       case 'Unknown OS':
  218.         // should catch Mac OS X compatible environment 
  219.         if (!empty($_SERVER['SERVER_SOFTWARE']
  220.             && preg_match('/Darwin/',$_SERVER['SERVER_SOFTWARE'])) 
  221.            // fall-through 
  222.         else 
  223.            $this->properties['OS_supports_flock'FALSE
  224.            break
  225.         }
  226.       default:
  227.         $this->properties['OS_supports_flock'TRUE;
  228.     }
  229.   }
  230.   
  231.   
  232.   /**
  233.    * Resets the object so it's able to take a new xml sting/file
  234.    *
  235.    * Constructing objects is slow.  If you can, reuse ones that you have used already
  236.    * by using this reset() function.
  237.    */
  238.   function reset({
  239.     $this->_lastError   = '';
  240.   }
  241.   
  242.   //-----------------------------------------------------------------------------------------
  243.   // XPathBase                    ------  Helpers  ------                                    
  244.   //-----------------------------------------------------------------------------------------
  245.   
  246.   /**
  247.    * This method checks the right amount and match of brackets
  248.    *
  249.    * @param     $term (string) String in which is checked.
  250.    * @return          (bool)   TRUE: OK / FALSE: KO
  251.    */
  252.   function _bracketsCheck($term{
  253.     $leng strlen($term);
  254.     $brackets 0;
  255.     $bracketMisscount $bracketMissmatsh FALSE;
  256.     $stack array();
  257.     for ($i=0$i<$leng$i++{
  258.       switch ($term[$i]{
  259.         case '(' 
  260.         case '[' 
  261.           $stack[$brackets$term[$i]
  262.           $brackets++
  263.           break;
  264.         case ')'
  265.           $brackets--;
  266.           if ($brackets<0{
  267.             $bracketMisscount TRUE;
  268.             break 2;
  269.           }
  270.           if ($stack[$brackets!= '('{
  271.             $bracketMissmatsh TRUE;
  272.             break 2;
  273.           }
  274.           break;
  275.         case ']' 
  276.           $brackets--;
  277.           if ($brackets<0{
  278.             $bracketMisscount TRUE;
  279.             break 2;
  280.           }
  281.           if ($stack[$brackets!= '['{
  282.             $bracketMissmatsh TRUE;
  283.             break 2;
  284.           }
  285.           break;
  286.       }
  287.     }
  288.     // Check whether we had a valid number of brackets.
  289.     if ($brackets != 0$bracketMisscount TRUE;
  290.     if ($bracketMisscount || $bracketMissmatsh{
  291.       return FALSE;
  292.     }
  293.     return TRUE;
  294.   }
  295.   
  296.   /**
  297.    * Looks for a string within another string -- BUT the search-string must be located *outside* of any brackets.
  298.    *
  299.    * This method looks for a string within another string. Brackets in the
  300.    * string the method is looking through will be respected, which means that
  301.    * only if the string the method is looking for is located outside of
  302.    * brackets, the search will be successful.
  303.    *
  304.    * @param     $term       (string) String in which the search shall take place.
  305.    * @param     $expression (string) String that should be searched.
  306.    * @return                (int)    This method returns -1 if no string was found,
  307.    *                                  otherwise the offset at which the string was found.
  308.    */
  309.   function _searchString($term$expression{
  310.     $bracketCounter 0// Record where we are in the brackets. 
  311.     $leng strlen($term);
  312.     $exprLeng strlen($expression);
  313.     for ($i=0$i<$leng$i++{
  314.       $char $term[$i];
  315.       if ($char=='(' || $char=='['{
  316.         $bracketCounter++;
  317.         continue;
  318.       }
  319.       elseif ($char==')' || $char==']'{
  320.         $bracketCounter--;
  321.       }
  322.       if ($bracketCounter == 0{
  323.         // Check whether we can find the expression at this index.
  324.         if (substr($term$i$exprLeng== $expressionreturn $i;
  325.       }
  326.     }
  327.     // Nothing was found.
  328.     return (-1);
  329.   }
  330.   
  331.   /**
  332.    * Split a string by a searator-string -- BUT the separator-string must be located *outside* of any brackets.
  333.    * 
  334.    * Returns an array of strings, each of which is a substring of string formed
  335.    * by splitting it on boundaries formed by the string separator.
  336.    *
  337.    * @param     $separator  (string) String that should be searched.
  338.    * @param     $term       (string) String in which the search shall take place.
  339.    * @return                (array)  see above
  340.    */
  341.   function _bracketExplode($separator$term{
  342.     // Note that it doesn't make sense for $separator to itself contain (,),[ or ],
  343.     // but as this is a private function we should be ok.
  344.     $resultArr   array();
  345.     $bracketCounter 0;  // Record where we are in the brackets. 
  346.     do // BEGIN try block
  347.       // Check if any separator is in the term
  348.       $sepLeng =  strlen($separator);
  349.       if (strpos($term$separator)===FALSE// no separator found so end now
  350.         $resultArr[$term;
  351.         break// try-block
  352.       }
  353.       
  354.       // Make a substitute separator out of 'unused chars'.
  355.       $substituteSep str_repeat(chr(2)$sepLeng);
  356.       
  357.       // Now determine the first bracket '(' or '['.
  358.       $tmp1 strpos($term'(');
  359.       $tmp2 strpos($term'[');
  360.       if ($tmp1===FALSE{
  361.         $startAt = (int)$tmp2;
  362.       elseif ($tmp2===FALSE{
  363.         $startAt = (int)$tmp1;
  364.       else {
  365.         $startAt min($tmp1$tmp2);
  366.       }
  367.       
  368.       // Get prefix string part before the first bracket.
  369.       $preStr substr($term0$startAt);
  370.       // Substitute separator in prefix string.
  371.       $preStr str_replace($separator$substituteSep$preStr);
  372.       
  373.       // Now get the rest-string (postfix string)
  374.       $postStr substr($term$startAt);
  375.       // Go all the way through the rest-string.
  376.       $strLeng strlen($postStr);
  377.       for ($i=0$i $strLeng$i++{
  378.         $char $postStr[$i];
  379.         // Spot (,),[,] and modify our bracket counter.  Note there is an
  380.         // assumption here that you don't have a string(with[mis)matched]brackets.
  381.         // This should be ok as the dodgy string will be detected elsewhere.
  382.         if ($char=='(' || $char=='['{
  383.           $bracketCounter++;
  384.           continue;
  385.         
  386.         elseif ($char==')' || $char==']'{
  387.           $bracketCounter--;
  388.         }
  389.         // If no brackets surround us check for separator
  390.         if ($bracketCounter == 0{
  391.           // Check whether we can find the expression starting at this index.
  392.           if ((substr($postStr$i$sepLeng== $separator)) {
  393.             // Substitute the found separator 
  394.             for ($j=0$j<$sepLeng$j++{
  395.               $postStr[$i+$j$substituteSep[$j];
  396.             }
  397.           }
  398.         }
  399.       }
  400.       // Now explod using the substitute separator as key.
  401.       $resultArr explode($substituteSep$preStr $postStr);
  402.     while (FALSE)// End try block
  403.     // Return the results that we found. May be a array with 1 entry.
  404.     return $resultArr;
  405.   }
  406.  
  407.   /**
  408.    * Split a string at it's groups, ie bracketed expressions
  409.    * 
  410.    * Returns an array of strings, when concatenated together would produce the original
  411.    * string.  ie a(b)cde(f)(g) would map to:
  412.    * array ('a', '(b)', cde', '(f)', '(g)')
  413.    *
  414.    * @param     $string  (string) The string to process
  415.    * @param     $open    (string) The substring for the open of a group
  416.    * @param     $close   (string) The substring for the close of a group
  417.    * @return             (array)  The parsed string, see above
  418.    */
  419.   function _getEndGroups($string$open='['$close=']'{
  420.     // Note that it doesn't make sense for $separator to itself contain (,),[ or ],
  421.     // but as this is a private function we should be ok.
  422.     $resultArr   array();
  423.     do // BEGIN try block
  424.       // Check if we have both an open and a close tag      
  425.       if (empty($openand empty($close)) // no separator found so end now
  426.         $resultArr[$string;
  427.         break// try-block
  428.       }
  429.  
  430.       if (empty($string)) {
  431.         $resultArr[$string;
  432.         break// try-block
  433.       }
  434.  
  435.       
  436.       while (!empty($string)) {
  437.         // Now determine the first bracket '(' or '['.
  438.         $openPos strpos($string$open);
  439.         $closePos strpos($string$close);
  440.         if ($openPos===FALSE || $closePos===FALSE{
  441.           // Oh, no more groups to be found then.  Quit
  442.           $resultArr[$string;
  443.           break;
  444.         }
  445.  
  446.         // Sanity check
  447.         if ($openPos $closePos{
  448.           // Malformed string, dump the rest and quit.
  449.           $resultArr[$string;
  450.           break;
  451.         }
  452.  
  453.         // Get prefix string part before the first bracket.
  454.         $preStr substr($string0$openPos);
  455.         // This is the first string that will go in our output
  456.         if (!empty($preStr))
  457.           $resultArr[$preStr;
  458.  
  459.         // Skip over what we've proceed, including the open char
  460.         $string substr($string$openPos strlen($string));
  461.  
  462.         // Find the next open char and adjust our close char
  463. //echo "close: $closePos\nopen: $openPos\n\n";
  464.         $closePos -= $openPos 1;
  465.         $openPos strpos($string$open);
  466. //echo "close: $closePos\nopen: $openPos\n\n";
  467.  
  468.         // While we have found nesting...
  469.         while ($openPos && $closePos && ($closePos $openPos)) {
  470.           // Find another close pos after the one we are looking at
  471.           $closePos strpos($string$close$closePos 1);
  472.           // And skip our open
  473.           $openPos strpos($string$open$openPos 1);
  474.         }
  475. //echo "close: $closePos\nopen: $openPos\n\n";
  476.  
  477.         // If we now have a close pos, then it's the end of the group.
  478.         if ($closePos === FALSE{
  479.           // We didn't... so bail dumping what was left
  480.           $resultArr[$open.$string;
  481.           break;
  482.         }
  483.  
  484.         // We did, so we can extract the group
  485.         $resultArr[$open.substr($string0$closePos 1);
  486.         // Skip what we have processed
  487.         $string substr($string$closePos 1);
  488.       }
  489.     while (FALSE)// End try block
  490.     // Return the results that we found. May be a array with 1 entry.
  491.     return $resultArr;
  492.   }
  493.   
  494.   /**
  495.    * Retrieves a substring before a delimiter.
  496.    *
  497.    * This method retrieves everything from a string before a given delimiter,
  498.    * not including the delimiter.
  499.    *
  500.    * @param     $string     (string) String, from which the substring should be extracted.
  501.    * @param     $delimiter  (string) String containing the delimiter to use.
  502.    * @return                (string) Substring from the original string before the delimiter.
  503.    * @see       _afterstr()
  504.    */
  505.   function _prestr(&$string$delimiter$offset=0{
  506.     // Return the substring.
  507.     $offset ($offset<0$offset;
  508.     $pos strpos($string$delimiter$offset);
  509.     if ($pos===FALSEreturn $stringelse return substr($string0$pos);
  510.   }
  511.   
  512.   /**
  513.    * Retrieves a substring after a delimiter.
  514.    *
  515.    * This method retrieves everything from a string after a given delimiter,
  516.    * not including the delimiter.
  517.    *
  518.    * @param     $string     (string) String, from which the substring should be extracted.
  519.    * @param     $delimiter  (string) String containing the delimiter to use.
  520.    * @return                (string) Substring from the original string after the delimiter.
  521.    * @see       _prestr()
  522.    */
  523.   function _afterstr($string$delimiter$offset=0{
  524.     $offset ($offset<0$offset;
  525.     // Return the substring.
  526.     return substr($stringstrpos($string$delimiter$offsetstrlen($delimiter));
  527.   }
  528.   
  529.   //-----------------------------------------------------------------------------------------
  530.   // XPathBase                ------  Debug Stuff  ------                                    
  531.   //-----------------------------------------------------------------------------------------
  532.   
  533.   /**
  534.    * Alter the verbose (error) level reporting.
  535.    *
  536.    * Pass an int. >0 to turn on, 0 to turn off.  The higher the number, the
  537.    * higher the level of verbosity. By default, the class has a verbose level
  538.    * of 1.
  539.    *
  540.    * @param $levelOfVerbosity (int) default is 1 = on
  541.    */
  542.   function setVerbose($levelOfVerbosity 1{
  543.     $level = -1;
  544.     if ($levelOfVerbosity === TRUE{
  545.       $level 1;
  546.     elseif ($levelOfVerbosity === FALSE{
  547.       $level 0;
  548.     elseif (is_numeric($levelOfVerbosity)) {
  549.       $level $levelOfVerbosity;
  550.     }
  551.     if ($level >= 0$this->properties['verboseLevel'$levelOfVerbosity;
  552.   }
  553.    
  554.   /**
  555.    * Returns the last occured error message.
  556.    *
  557.    * @access public
  558.    * @return string (may be empty if there was no error at all)
  559.    * @see    _setLastError(), _lastError
  560.    */
  561.   function getLastError({
  562.     return $this->_lastError;
  563.   }
  564.   
  565.   /**
  566.    *