PHP Classes

File: XbertClass.txt

Recommend this page to a friend!
  Classes of Bob Wedwick   PHP Expert System Engine   XbertClass.txt   Download  
File: XbertClass.txt
Role: Documentation
Content type: text/plain
Description: Xbert rule set
Class: PHP Expert System Engine
Ask questions and make decisions based on answers
Author: By
Last change:
Date: 6 years ago
Size: 58,707 bytes
 

Contents

Class file image Download
Note: #.... represents the end of an 'if' statement # <--- represents the end of a loop /* Author: Bob Wedwick USE: in its simplest form require_once "XbertClass.php"; $exp = new Xbert; $exp->RunXbert('SomeFileName.xxx'); Copyright (C) 2017 Software Installation Services, Inc. Author: Bob Wedwick, Phoenix, AZ 602-449-8552 bobwedwick at gmail dot com. This program is free software: you can redistribute it or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the license or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. See http://www.gnu.org/licenses/licenses.en.html for a copy of the GNU General Public License. This class, Xbert, is a type of artificial intelligence engine often referred to as an expert system or inference engine. I first learned of the concepts in an article from "Information Center" magazine dated March 1987. For purposes of explaining how to use this class, "rule set" is defined to be either a CSV file or a mySql table. A "knowledge base" is comprised of one or more rule sets. A knowledge base is a type of decision tree. Xbert uses a knowledge base to pose questions to a user (or proxy user) to arrive at one or more conclusions based on which paths chosen. It optionally traces the logical steps used to arrive at the conclusion in a trace file or trace table, so it is fairly easy to check the steps involved. Separate functions in the user script can be used to act like a proxy user that looks up pre-determined answers from any source such as INI files, SQL tables, built in arrays, or text files. Multiple human experts can be involved in creating rule sets, so the combined intelligence of a knowledge base may exceed that of any single person. Xbert knowledge bases can be created without the need to learn a complicated programming language. It is easy to start with one rule set and then expand to more complex groups of rule sets as knowledge is added and more choices arise. While learning how to use this, you are encouraged to read and modify those supplied as examples. A knowledge base may be just one rule set or may be comprised of rule sets from different directories on one computer, or from a network, or tables from different databases, if it makes sense to do that. Thus a complete knowledge base for a large project may be comprised of numerous subordinate rule sets from various locations. In this way a large project can be broken down into subsets, and each subordinate rule set can be run and tested by itself. A knowledge base may use rule sets from both CSV files and mySql tables. If SQL tables are used, a db connection needs to be established before attempting to read or write to any tables. Xbert works with one db connection at a time, so if more than one is used, managing multiple db connections is left to the calling script. Through question and answer, each rule set continues line by line until either a new node is found or the result of a command produces a 'false' condition. When a false condition is determined, all subsequent lines are ignored until a node equal to or less than the false-producing node value is found. If none is found that rule set ends. Thus one branch of a decision tree advances asking questions and gathering input for making decisions or displaying results. This is best understood by studying examples which are included in the package. Knowledge bases range from being very simple to extremely complex. Each rule set works like tree branches made up nodes and subordinate nodes. By default, each rule set begins with node level 1. Node values less than one are not valid. Each subordinate node has a floating point numeric value that is greater than or equal to the prior node. There is no practical limit to the node values, and they do not have to be sequential but may have gaps to allow for easier line insertions. Fairly detailed rule sets have been developed using node levels less than 20. The higher the number, the farther out on a limb of the decision tree. Three columns are used for a line of a rule set, and any columns may be blank. Column 1. Node-Id - Node identifier: also refered to as a node. When the "Node Id" column is blank, the line belongs to the branch of the last known node. Each node with a value is like a new branch of a tree. The node can be a calculated value if it makes sense to do so. It can be <value1>+<value2> <value1>-<value2> +<value2> -<value2> ++ -- If <value1> is missing, math is applied to the current node value. Column 2. Command - a command to the inference engine. A blank command adds a free form text line in Column 3, to the next message going to the user. Column 3. Text Column - free form text. Its use depends on the command in column 2. This is explained below with the list of commands. Any line without at least three columns is ignored. A spreadsheet may be used to create or edit the CSV rule set file by saving the spreadsheet as a CSV file. The spreadsheet needs to have at least three columns. A spreadsheet program (like Open Office) may require the file extension to be ".csv" which can later be renamed. When the combination of double quote, comma, double quote (",") string is found within the first 1K bytes of a CSV file, it is treated as a standard CSV file and only the first three columns are used. Column 3 may then contain commas and double quotes embedded in the text. Columns 4 and greater are ignored may contain indexes, comments or whatever. When a text processor (not a word processor) is used to create or edit a CSV rule set, each active line should have at least three columns separated by commas; otherwise the line is ignored. All text, including additional commas, after the 2nd column is included in the 3rd column. Thus there is no 4th column. Xbert allows for some things that are not found in standard CSV files. Those things include not requiring columns to be surrounded with double quotes ("), double quotes embedded in text do not have to be doubled to get one (""),and blank lines or lines with just one or two columns are allowed. The CSV file name may have any extension. Examples include '.xbt' and '.know'. When a mySql table is the source of a rule set, it should have at least three columns suggested to be varchar(20), varchar(20), and varchar(80). Any columns after the third are not read, so they can be for any purpose such as for indexing and comments. Fields are referenced by position, so the field names do not matter. Tables are read one line at a time sequentially. A calling script specifies the name of the initial CSV file or mySql table. A CSV file may be in the same directory as the calling script or in another directory. Any rule set may call subordinate rule sets, analogous to PHP functions. When a subordinate rule set is finished, its local variables are released and control is returned to its calling rule set. When a specific directory is not part of the name of the subordinate rule set, the Xbert looks for the subordinate rule set in the directory being used. The Xbert optionally traces the user's responses in a trace text file or trace table bearing a name of the designer's choice. That name may be defined in a variable. If no name is provided the default name is "Trace" plus a date-time string and a '.trace' extension for a CSV file. Tracing can be turned on and off at various places in any rule set, however the trace file remains open and locked. The Xbert interacts with a user by sending text messages to the calling PHP script. It is up to the calling script to provide a user interface, which may be a console application, browser application, proxy user or some other automated interface. A user interface function named "UserInterface" is assumed by default. There can be more than one user interface function used in the calling script, and it can be changed from rule sets. A simple example of a console interface is included. The calling script continuously receives messages, and the user sends responses until all rule sets are finished. Once the last rule set ends, and if there is a function named "Finalize" in the calling script, that function is called. It's use is up to the designer. For example it may be used to display some final messages like those for conclusion or summary or to send an email message. Two types of variables can be used anywhere in rule sets: global and local. Variable names are distinguished by a single word with no spaces enclosed in < and >, for example <my_name>. The < and > marks are included in the name of the variable, so <> is a valid local variable name which is used to hold the latest user's response to questions. Variable names are not case sensitive. Global variables may be set and changed by all rule sets. Local variables are local to the rule set declaring them. When a rule set ends, its local variables go away. If both global and local variables have the same name, the local variable prevails. The $ is not used for variable names because in some text areas the $ may be part of the message. For example if $<money> appears on a line, the dollar sign and variable's value for <money> is used. Named variables may be used in any of the three columns on a line. If used for the node's value, (column 1), the symbolic name is replaced with its numeric value. When a variable name is used for the command, (column 2) it is not case sensitive and its value must be either a valid command, comment mark or blank. When embedded in message text, (column 3) named variables that are found are replaced with the values of the variables when the variable is found. If a word looks like a variable with < and > but does not exist, the word is left unchanged in the text. There is no limit to how many named variables can be used in the text (column 3) of a message to the user or in a 'conclusion' or trace message (see below). *** Rule set commands (column 2) *** Some fairly small rule sets have been done using just the these commands: ASK, ASKN, ASKY, PAU, and FAL. The next two commands are unique to their first four characters. For easier readability, additional characters are optional including spaces before and after the command. ASKNo displays any user message and prompts the user for a "NO/Yes" response. If "N" is the first letter of the response or the response is blank, the current node remains active. ASKYes displays any user message and prompts the user for a "YES/No" response. If "Y" is the first letter of the response or the response is blank, the current node remains active. The remaining commands are unique to their first three characters. For easier designer readability, additional characters including spaces are optional. ASK displays any user message and prompts the user for a response. The response is held in the local variable named "<>". The value in the response variable can be a string or a numeric value depending upon how the user responds. The response can be copied to another variable or tested using subsequent commands referencing the variable named <>. PAUse - displays any accumulated user message and asks the user to press the ENTER key. If you respond to any of the above user requests with the "abort" word, the Knowledge base ends immediately. That way if users do not wish to continue for any reason, they do not have to step through the rest of the Knowledge base. The text of the expected user responses are stored in local variables and can be changed in rule sets to reflect different languages. Variables <_enter_> <_yesno_> <_noyes_> <_reply_> are the pre-assigned variables for this purpose. For example: ,loc,<_yesno_> JES/ne changes what the ASKY command expects for a user response. The [] characters used below imply optional items. FALse [<variableName>, a numeric value, --, -num, or -<varname>] - discontinues the current node reading lines to find the next line with a node value <= the given value. Until that is found, all subsequent lines are ignored. When no value is given, the currently active node value is implied. GOTo - same as FALse. CSV filename - opens a new CSV rule set and transfers control to it. TABle tablename - opens a new database table as a rule set and transfers control to it. A mySql connection must have been previously set up from the calling program. The tablename may have to be completely qualified like database.tablename. FUNction function name - name of the function in the user script to call to get a reply to a message. The function name is stored as a rule set variable, so it can be different for each different rule set. This would replace "UserInterface" for user interaction. # ; or // in column 1 or column 2 identifies a comment line, and it is ignored. Multiline comments begin with slash* being anywhere on the line. They end after *slash appears anywhere on the line. END immediately ends the current rule set. GLO <name> [value] - creates a global variable with the given name and assigns the optional value to it. Values may be numeric or string. If the variable is used as a node, it must evaluate to numeric. These variables are visible to and can be changed by all rule sets in use. If [value] is not given, the value from the last user response is stored. LOCal <name> [value] - creates a local variable with the given name and assigns the optional value to it. Values may be numeric or string. If the variable is used as a node identifier, it must evaluate to numeric. These variables are visible to and can be changed only by all rule set declaring them. If value is not given, the value from the last user response is stored. ISGlobal <variable name> true when a global variable exists by that name. ISLocal <variable name> true when a local variable exists by that name. ISSet <variable name> true when either a global or local variable exists by that name. PUT string - writes a string to the trace file or table when trace is active even when trace has been turned off after initiating it. The user does not see the string being written. For commands below, value1 and value2 can be either constants or variables. Constants may be either numeric or string. If the value is the name of a variable, the content of the variable is used. When value2 is missing, the value from the latest response <> is used. When a command produces a false result, it is treated like the FAL command. Strings are compared case insensitive. EQU value1 value2 -- true if value1 = value2. GRE value1 value2 -- true if value1 > value2 GTE value1 value2 -- true if value1 >= value2 LES value1 value2 -- true if value1 < value2 LTE value1 value2 -- true if value1 <= value2 LIKe value1 value2 -- true if either value is a substring of the other NEQ value1 value2 -- true if value1 != value2. For the following math commands, if [variable3] is given the result is saved there. If [variable3] does not exist, it is created as a local variable. If [variable3] is not given, the result is saved in the local response variable <>. ADD value1 value2 [variable3] -- value1 plus value2 -> [variable3] SUBtract value1 value2 [variable3] -- value1 minus value2 -> [variable3] MULtiply value1 value2 [variable3] -- value1 * value2 -> [variable3] DIVide value1 value2 [variable3] -- value1 / value2 (value2 must not be zero) -> [variable3] MAXimum value1 value2 [variable3] -- the greater of value1 or value2 -> [variable3] MINimum value1 value2 [variable3] -- the lesser of value1 or value2 -> [variable3] CON (conclusion) adds a text in column 3 to the global variable <conclusion>. This is not shown to the user. How this is used is up to the designer. For example it can be retrieved in the 'Finalize' function for a final message or some sort of email. CAL [message] - when the function "UserCall" exists in the starting script, it is called with the optional message. Its use is entirely up to the designer but can be used for such things as changing directories, changing database connections, or copying CSV files from internet. There is no implicit user interaction. The next commands give rule set designers more control and more insight into what is happening with their rule sets. The controls for debug and trace commands are stored as local variables so are limited to the rule set requesting them. The next two commands are unique to their first four characters. DBON [optional function name] Enable a user defined debug function in the calling script. By default a function named "UserDebug" is assumed to be in the calling script. The calling script can have several different debug functions. When a rule set changes a debug function name, that function name replaces the current name in the current rule set. When that rule set ends, the prior name is again used. If the debug user function exists in the calling script, it is called after each non-blank command from column 2. The user function in the calling script has the ability to access any control variables to find out the current state of Xbert. Debug can be turned on and off in various places in rule sets, so you can debug only those things of interest. When a rule set is first opened, debugging is not active. DBOFf turns debuging off for the current rule set only. TRFile [name[.extension]] Turns on tracing the commands used to arrive at a conclusion. Commands that affect the logical flow are traced plus the ASK command. The first time a trace file is opened, it is truncated and locked. If someone else is using a file by this name, an error is produced. Tracing can be turned on and off in various places in rule sets, so you can trace only those things of interest. When a rule set is first opened, tracing is not active. If a trace file is already open, any new TRF name is ignored. If [name] is not given the file name defaults to "Trace" with a date-time stamp. If a text file is used, the name defaults to a ".trace" extension. When there is more than one simultaneous user of the same rule sets, each user should either give the trace file a unique name or let Xbert assign a default name; otherwise if it is already used, by someone else the locked file generates an error. TRTable [[database.]table name] - traces commands to an SQL table; otherwise it acts like tracing to a text file. If a trace table is already open, any new TRT name is ignored. If someone else is using a table by this same name, this produces an error. If either a trace file or trace table was used, a message to that effect is made available to the FINALIZE function in the calling script. TROff - temporarily stops tracing to either the file or the table being used. Closing the file or table and releasing the lock does not take place until the last rule set is finished. RESpond <variable> or constant value - automatically fills in the response to just the next ASK, ASKN, ASKY, or PAU command with a constant like 'Yes' or 'No' or a value from a previously defined local or global variable. If a variable name is given and a match is not found, the automatic reply is not activated, which is an easy way to disable it. Using a variable name instead of a constant value for a response allows a designer to effectively turn on or turn off the automatic response. During the design phase, this allows a rule set designer to avoid the tedium of stepping through many inquiries to reach the desired part of a rule set. EXCeption 'off' - turns exception handling off. Anything other than 'off' turns it on. When exceptions are on, they are on for all rule sets. By default, exceptions are off. The calling script needs to have code for the 'try ... catch' logic. A summary message is prepared during the processing which lists which rule sets were used, errors encountered, and the name of any trace file or table that was used. This can be optionally accessed by means of a FINALIZE function in the calling script. The following public functions may be useful in the user's debug function, or user interface functions like UserInterface, UserCall and Finalize. function CloseRuleSet() function Conclusion($str=false) function DbConnection($db= false) function ExecuteSql($sql) function GlobalValue($word, $value=null) function LocalValue($word, $value = null) function OpenCsvRuleSet($ruleSetName) function OpenTableRuleSet($ruleSetName) function RuleSetControlValue($name, $value=null) function SummaryMessage($str=false) function ValueForAVariable() function VariableName($word) function WhereAmI() function WordValues(&$word, &$arr) function WriteToTrace($traceMsg = false) The following functions complete the class and are shown here for documentation but may be called from the user script with the understanding that some prior setup may be needed. function AskUser($command) function CommentLine() function CommentToken() function ComparisonCommand($c) function ControlCommand($c) function DebugOn() function ErrorHandler($errMsg = '') function GeneralControlValue($name, $value=null) function GetAddToMessage($name, $str=false, $nl = true) function GetFromCsv() function GetFromTable() function GetNextLine() function GotoRelative() function HasValue($w='') function InteractionCommand($c) function IssetCommand($c) function LocalControlArray() function MathCommand($c) function MathResult($r) function NodeValue($n) function NormalCommand($word) function NormalVariableName($name) function OpenCloseCommand($c) function OpenTraceFile() function OpenTraceTable() function ParseLine($line=false) function ParseNode($n) function ParseText($str = false) function ProcessOneLine() function ProcessRuleSets() function RemoveIndent($text) function ReplaceAllVariables(&$str, &$arr) function RuleSetNumber() function RunXbert($ruleSet, $csv=true) function SaveFunctionName($ruleSetName, $functionName) function SeekNewNode($doit = false, $node = false) function SetResponse($value) function SqlSource() function StandardCsvFile() function TableExists($dbTable=false) function TraceMsgToTable($str) function TraceOnToFile() function TraceOnToTable() function UniqueName($name) function VariableName($word) function WordVariables($word) function WrapUp() function WriteToDebug() */ ### Define Xbert Class # variables are public so they can be referenced by the calling script # the abort word # auto reply flag or value # command part (column 2) of the current line # array of 4-char commands # make array of "if" commands # a conclusion message # make array of control commands # current rule set line unedited contents - false when at end of a rule set # data time for the initial rule set # db connection # error message # array of global variables # array of 'is set' commands # array of math commands available # new line characters # array of user interaction commands # node ID (column 1) part of the current line # array of Open or Close commands # array for control of multiple rule sets # summary message optionally used in a Finalize function # text part of the line, 3rd column # text of the line after first word in column 3 # message lines for output to the user # array of variable names in the current text part of the current line # array of words of the text of the current line ### AskUser() - ask the user for some response # clear the response variable - $this->LocalValue() # expect nothing # if command was ASKY # expected = <_yesno_> # elseif command was ASKN # expected = <_noyes_> # elseif command was PAUse # expected = <_enter_> # else # expected = <_reply_> # .... # what response to expect # append text and expected reply to the multiline message. - $this->GetAddToMessage() # clear the reply # if auto-reply was active - $this->GeneralControlValue() # remember it was an auto reply # clear the auto reply flag # else # get the named user interface function - $this->RuleSetControlValue() # send the user message and return reply value from the user function - $this->GeneralControlValue() # .... # if reply to ASKN or ASKY was expected # if reply is blank # set it to the expected response # .... # hold just the 1st char of the reply in upper case # if reply does not match what is expected # set the flag to advance to a new node - $this->SeekNewNode() # .... # .... # if the user's reply is blank # make it one space # elseif numeric # set it to float # .... # if trace is active - $this->RuleSetControlValue() # start the trace message with the user message - $this->GeneralControlValue() # add user's reply to the trace message # write the trace message to trace destination - $this->WriteToTrace() # .... # save the user's reply in the response variable <> - $this->SetResponse() # end function ### CloseRuleSet() - close a rule set, either CSV or mySql # if there is a trace file name - $this->RuleSetControlValue() # if it is unique to this rule set - $this->UniqueName() # unlock it - $this->RuleSetControlValue() # close the trace file - $this->RuleSetControlValue() # .... # .... # send closed to the summary message - $this->SummaryMessage() # delete the current rule set control group # end function ### CommentLine() - return true when current line is any type of comment # assume this line is not a comment # if current line has / * anywhere on it - $this->GeneralControlValue() # set the flag to begin a multiline comment - $this->RuleSetControlValue() # it is a comment -- return true # .... # if within a multiline comment - $this->RuleSetControlValue() # it is a comment - return true # if current line has * / anywhere - $this->GeneralControlValue() # remove flag for the multiline comment - $this->RuleSetControlValue() # .... # .... # true if any type of comment token is being used - $this->CommentToken() # return result # end function ### CommentToken() - true when the current line contains a comment token in column 1 or 2 # explode the current line into 3 columns - $this->GeneralControlValue() # if fewer than 3 columns in the current line # it is a comment # else # combine the node and command for testing # replace all variables in the node and command - $this->ReplaceAllVariables() # replace # // or ; # true if the length changed # .... # return result # end function ### ComparisonCommand() - "if" comparison commands stop processing a node when they are false # assume any comparisons are good - later comparisons are in lower case # get the value of word1 - $this->HasValue() # if second word is missing - $this->HasValue() # use the latest response - $this->LocalValue() # give second word the value of the latest <> - $this->HasValue() # .... # if values are not showing for either first word or second word # error should abort with a message - $this->ErrorHandler() # elseif EQU # true if the two words are equal # elseif GRE # true if first word > second word # elseif GTE # true if w1 greater or equal to w2 # elseif LES # true if first word < second word # elseif LTE # true if w1 less or equal w2 # elseif LIKe # true if either word is a substring of the other # elseif NEQ # true if w1 not equal w2 # .... # if trace is active - $this->RuleSetControlValue() # message reflects what was done and the result # write the trace message to trace destination - $this->WriteToTrace() # .... # seek a new level when any above failed - $this->SeekNewNode() # end function ### Conclusion($str) - optionally add to the conclusion and return it # fetch the complete conclusion message - $this->GetAddToMessage() # return the message # end function ### ControlCommand() - process a control command # if ending the rule set # close this rule set - $this->CloseRuleSet() # return # .... # get the value of first word # get value of var name 1 # if going to a node - GOTo or FAL and word1 is numeric # check for relative adjustments # if first word is blank # use the current level - $this->RuleSetControlValue() # else # get numeric value of the first word # if less than 1 # error - $this->ErrorHandler() # .... # .... # if trace is active - $this->RuleSetControlValue() # message reflects what was done and the result # write the trace message to trace destination - $this->WriteToTrace() # .... # set the new level to seek - $this->SeekNewNode() # if sending a string to the trace file - PUT # write it to trace output - $this->WriteToTrace() # elseif exception handling on or off - EXC # if w1 says 'off', exceptions are set off otherwise on # set the state for using exceptions # elseif there is first word # make default variable name for GLO and LOC when < and > are missing # if naming the user function to call # save the function name # elseif automatic response is being set - RES # if first word is a variable name # if it does not have a local value - $this->LocalValue() # use the global value - $this->GlobalValue() # .... # else # set the auto reply to first word # .... # elseif setting a global variable - GLO # set a global variable by name - $this->GlobalValue() $this->ValueForAVariable() # elseif setting a local variable - LOC # set a local variable by name - $this->LocalValue() $this->ValueForAVariable() # .... # else # first word is blank -- fatal error - $this->ErrorHandler() # .... # end function ### DbConnection() - optionally set the db connection variable and return the current connection. # if there is a db resource passed # if ping works # save it in local values - $this->GeneralControlValue() # .... # .... # return the dbconnection resource - $this->GeneralControlValue() # end function ### DebugOn() - enable a debug function with optional name # if no debug file name is passed in first word of the text # give it the default name UserDebug # .... # save it's name in variables - $this->SaveFunctionName() # turn debug on # end function ### ErrorHandler() - fatal error was encountered # add rule set and line number to the error message # save the message in the summary message also - $this->SummaryMessage() # add where we are to the error message - $this->WhereAmI() # enter the error in error variable - $this->RuleSetControlValue() # if throwing exceptions - $this->RuleSetControlValue() # do so and that ends things - Exception() # else # enter a user message - $this->GeneralControlValue() # if the user interface function exists - $this->RuleSetControlValue() # tell the user with a PAUse command - $this->AskUser() # else # echo the message to the console # .... # .... # program ends gracefully - $this->WrapUp() # end function ### ExecuteSql() - execute an SQL statement # execute the query - $this->GeneralControlValue() # if it failed # fatal error - $this->ErrorHandler() # .... # end function ### GeneralControlValue() - optionally set and return a value in general controls # if the variable exists in general variables # if setting the value in general variables # do that # .... # retrieve the value # else # not a general variable # .... # return result # end function ### GetAddToMessage() - optionally add to and return a message # if there is message to add # get the message string - $this->GeneralControlValue() # add the string to the message # if adding a new line, do that # save the appended message - $this->GeneralControlValue() # .... # fetch the complete message - $this->GeneralControlValue() # return the message # end function ### GetFromCsv() - get next line from a CSV file # hold the csv file handle- $this->RuleSetControlValue() # if we can read the next line # return true that we got a line # count how many csv fields # if a standard quoted CSV file- $this->RuleSetControlValue() # limit the count to 3 # .... # start with the 1st column trimmed # if there is a second column # append it trimmed # for 3 or more columns # append them not trimmed # <--- # .... # save the whole line in the current line variable - $this->GeneralControlValue() # else # no line was read, so return false # .... # return result # end function ### GetFromTable() - get the next line from a table # if we can read the next row - mysql_fetch_assoc - $this->RuleSetNumber() # make it three columns separated with commas # save it to currentLine - $this->GeneralControlValue() # return true when a good line was found # else # no line was read, so return false # .... # return result # end function ### GetNextLine() - get the next line from whichever source. Three columns comma separated # clear the current line - $this->GeneralControlValue() # if source is a mySql table - $this->SqlSource() # get the line from a SQL table - $this->GetFromTable() # else # get the line from CSV - $this->GetFromCsv() # .... # returns false when at end of a rule set # end function ### GlobalValue() - given an input string with <> and an optional value # if a value is sent # save it in global variable array # .... # if key is found in global variables array # return the value, it was found # else # return false - it was not found # .... # return what is found or false # end function ### GotoRelative - make adjustments when a GOT or FAL command relative to current node # if not blank # get 1st character # if 1st character is - ( the only sensible relative adjustment) # get current node value # if -- # subtract 1 # else # subtract the value # .... # elseif + # treat like being blank # .... # .... # end function ### HasValue() - return the lower case value of a string or convert to float - false when blank # if strlen == 0 # set it to false # else # if the string is numeric # make it float data type # else # make it lower case # .... # .... # return the value # end function ### LocalControlArray() - create a control array local to the rule set # get the next rule set index # create an array of ruleSetCount sensitive variable names # if the initial rule set # set to initial values # else # throw exceptions and user interaction words are copied from prior controls # interaction words can be changed in each rule set # .... # end function ### InteractionCommand() - process a user interaction command and clear the user message. # if adding to the conclusion # add text to the conclusion - $this->Conclusion() # elseif calling the user # if the function exists # call the user with the optional message # else # fatal error - $this->ErrorHandler() # .... #else # interact with the user - $this->AskUser() # clear the message string - $this->GeneralControlValue() # .... # end function ### IssetCommand() - process an Is Set command # get the value of variable 1 # global is true when variable 1 is set as a global variable - $this->GlobalValue() # local is true when variable 1 is set as a local variable - $this->LocalValue() # if looking for a global var - ISG # return its state # elseif looking for a local var - ISL # return its state # else must looking for either var - ISS # return true if either is set # .... # if trace is active - $this->RuleSetControlValue() # message reflects what was done and the result # write the trace message to trace destination - $this->WriteToTrace() # .... # when it fails, seek a new level - $this->SeekNewNode() # end function ### LocalValue() - get or set a local variable given an input word string with <> # get the current rule set number - $this->RuleSetNumber() # if a value is sent # save it in local variables - $this->RuleSetNumber() # .... # if key is found in local variables array - $this->RuleSetNumber() # return the value - $this->RuleSetNumber() # else # false - it was not found # .... # return result # end function ### MathCommand() - process a math command # get copies of first and second word values from the text column # if two words are there # test MAX and MIN using lower case # if MAX # return the greater of first or second word # elseif MIN # return the lesser of first or second word # elseif first and second words are numeric # if ADD word1 plus word2 # return result # elseif SUB word1 minus word2 # return result # elseif MUL word1 * word2 # return result # elseif DIV word1 / word2 # if dividing by Zero # fatal numeric error - $this->ErrorHandler() # .... # return result # .... # else # fatal numeric error - $this->ErrorHandler() # .... # else # first word is blank - fatal error- $this->ErrorHandler() # .... # set the math result - $this->MathResult() # end function ### MathResult() - set the result of a math command in the appropriate variable # if there is a variable name for the optional destination - $this->VariableName() # if var3 is blank, use the reponse variable # if there is a local variable by this name - $this->LocalValue() # update the local variable - $this->LocalValue() # elseif there is a global variable by this name - $this->GlobalValue() # update the global variable - $this->GlobalValue() # else # create a local variable - $this->LocalValue() # .... # end function ### NodeValue() - return the value of the node (column 1) from the current line # trim the node value # if blank # immediately return false # else # replace any variables with their values - $this->ReplaceAllVariables() # parse text of the node into up to 3 values - $this->ParseNode() # hold the current node value - $this->RuleSetControlValue() # if just '++' for signs # add one to current level # elseif just' --' for signs # subtract one from current level # elseif +- or -+ # return the current node # else # if no value for element 0 # make it the current node # .... # default return to element 0 # if no value in element 2 # make it zero # .... # if a + sign is in signs # add value 0 to value 2 # else if a - sign in signs # subtract value 2 from value 0 # .... # .... # if resulting value is less than 1 # fatal error - $this->ErrorHandler() # .... # .... # return result # end function ### NormalCommand() - normalize the command portion of a line # if the string passed is not blank # replace the word when it is a variable - $this->ReplaceAllVariables($word, $arr) # make the string uppercase and at least 4 chars long # save 1st 4 chars of the command # if not in the 4-char command array # make it just 3 chars # .... # else # clear the command variable # .... # return the result # end function ### NormalVariableName() - return the string passed as a normal variable name with <> around it # remove any < and > # put on < and > # variable names are always lower case -- thus case insensitive # return the normal name # end function ### OpenCloseCommand() - examine commands for opening or closing things # if turning debug on - DBON # set controls- $this->DebugOn() # elseif turning debug off - DBOF # set controls - $this->RuleSetControlValue() # elseif opening a trace file - TRF # try to open and lock a new trace text file - $this->OpenTraceFile() # try to activate a trace file - $this->TraceOnToFile() # write to trace when tracing - $this->WriteToTrace() # elseif opening a trace table - TRT # try to open a trace table - $this->OpenTraceTable() # try to activate a trace table - $this->TraceOnToTable() # write to trace when tracing - $this->WriteToTrace() # elseif turning off the current trace file - TROf # write to trace when tracing - $this->WriteToTrace() # set trace off - $this-> RuleSetControlValue() # elseif opening a new CSV rule file # open it - $this->OpenCsvRuleSet() # elseif opening a new mySql rule table - TABle # open it - $this->OpenTableRuleSet() # .... # end function ### OpenCsvRuleSet() - open a CSV rule set # if allowed to open the file as read only # enter a line in the summary message - $this->SummaryMessage() $this->WhereAmI() # create a copy of the current local control entries - $this->LocalControlArray() # flag it for CSV input - $this->RuleSetControlValue() # set the file name in controls from first word - $this->RuleSetControlValue() # save the file handle - $this->RuleSetControlValue() # flag true when a standard CSV file with any extension - $this->StandardCsvFile() # else # fatal error - $this->RuleSetControlValue() # $this->ErrorHandler() # .... # end function ### OpenTableRuleSet() - open a table rule set # if no db connection # fatal error # .... # if the table exists - $this->TableExists() # enter a line in the summary message - $this->SummaryMessage() # if you can query for a complete data set # create a copy of the current local control entries - $this->LocalControlArray() # hold the data set - $this->RuleSetControlValue() # flag for SQL input - $this->RuleSetControlValue() # put the rule set name in controls - $this->RuleSetControlValue() # .... # else # fatal error - $this->ErrorHandler() # .... # end function ### OpenTraceFile() - open and lock a trace text file # if a trace TABLE has been activated in this rule set # fatal error - we can not open a trace file now - $this->ErrorHandler() # .... # if there is already a trace file name - $this->RuleSetControlValue() # ignore the new name and return now # .... # if a trace file name is given in first word - $this->RuleSetControlValue() # save it's name in variables - $this->RuleSetControlValue() # else # build a default file name for tracing - $this->RuleSetControlValue() # .... # hold the text file name - $this->RuleSetControlValue()unexpected # if no extension on the file name # make it .trace # save it's name in variables - $this->RuleSetControlValue() # .... # if you can open it # lock it so others can not make entries while you have it # save the trace handle - $this->RuleSetControlValue() # flag that we are now tracing - $this->RuleSetControlValue() # flag that tracing is active - $this->RuleSetControlValue() # else # fatal error - we can not open a trace file - $this->ErrorHandler() # .... # end function ### OpenTraceTable() - open and lock a trace table # if a trace FILE has been activated in this rule set # fatal error - we can not open a trace table now - $this->ErrorHandler() # .... # if there is already a trace table name - $this->RuleSetControlValue() # ignore the new name and return now # .... # if there is a db connection - $this->GeneralControlValue() # if no trace table name is given in first word #build a default table name # .... # save it's name in variables - $this->RuleSetControlValue() # sql for dropping any existing table by name # execute the SQL - $this->ExecuteSql() # SQL for creating a new table # execute the SQL - $this->ExecuteSql() # write lock the table # execute the SQL - $this->ExecuteSql() # set trace and active flags to 'on' - $this->RuleSetControlValue() # else # error - no db connection - $this->ErrorHandler() # .... # end function ### ParseLine() - parse a line for processing substituting variables where needed # clear the node and command variables # if a line is passed # explode the line into three parts - $this->RuleSetControlValue() # if at least 3 parts which there should always be # copy part [0] to the node control variable - $this->NodeValue() # normalize and copy the command - $this->GeneralControlValue() - $this->NormalCommand() # parse the col 3 text pieces of the line replacing variables as needed - $this->ParseText() # blank text is allowed for message spacing # .... # .... # end function ### ParseNode() - parse the node value to an array # the return array has up to 3 values # get length of the node string # begin with array element 0 # loop through the string character by charcter # get one character # if the character is a + or - # it goes in element 1 # elseif element 1 is used # add the character to element 2 # else # add the character to element 0 # .... # <--- # if element 0 is numeric # make it floating point # .... # if element 2 is numeric # make it floating point # .... # return the array # end function ### ParseText() - replace variables in text part (column 3) of a line, then save the edited line # some text uses variables which need to be replaced with their values # reset varnames and text values # var name index starts 0 # if something is passed # remove and save any leading indent - $this->RemoveIndent() # trim the string # parse the string into space delimited words # loop thru the words # NvP array for any variable names found # edit words and pull out variable names - $this->WordValues($word, $arr) # remember the resulting word # if any variables are in this word # for each var name found in one word, remember var names # save the variable name in NvP array # advance the variable count # <--- # else # check next var name # .... # build text and text2 # text2 is everything after the 1st word # <--- # build text and text 2 # .... # end function ### ProcessOneLine() - process one line from the rule set # if there is Node Id - $this->GeneralControlValue() # save it to the rule set node variable - $this->RuleSetControlValue() # .... # make a copy of the command # if no command # save the text as a line in the user message - $this->GetAddToMessage() # else if in the command involves user interaction # process an interaction command - $this->InteractionCommand() # else if in the math command array # process a math command - $this->MathCommand() # else if in the comparison command array # process the comparison command - $this->ComparisonCommand() # else if in the control commands array # process the control command - $this->ControlCommand() # else if in 'is set' commands array # process the 'is set' command - $this->IssetCommand() # elseif the command is in the On-Off array (trace and debug) # process an On Off command - $this->OpenCloseCommand() # else # fatal error for unknown command - $this->ErrorHandler() # .... # if not the DBON command and debug is on in this rule set - $this->RuleSetControlValue() # write information to the debug process - $this->WriteToDebug() # .... # end function ### ProcessRuleSets() - process lines from rule sets until the last set is done # loop while rule sets are active # if we can get the next line - $this->GetNextLine() # bump line number for the current rule set - $this->RuleSetNumber() # if not a comment line - $this->CommentLine() # parse the current line - $this->ParseLine() # if seeking a new node - $this->SeekNewNode() # if there is a Node Id on the current line - $this->GeneralControlValue() # get the new node value - $this->GeneralControlValue() # if new node <= node being sought # reset the seek flag - $this->RuleSetControlValue() # .... # .... # .... # if not seeking a new level - $this->SeekNewNode() # process this line - $this->ProcessOneLine() # .... # .... # else # close the current rule set - $this->CloseRuleSet() # .... # <--- # we are done -- out of rule sets - $this->WrapUp() # end function ### RemoveIndent() - remove any indentation from text and return it with modified text. Values by reference # replace tabs with 4 spaces # start an indent string # loop through the incoming string # if the next character is a space # append it to the indent string # else # done #.... # <--- # return the indent string # end function ### ReplaceAllVariables() - replace all variables in string of 1 or more words and update accompanying var array # parse the string into words # loop thru the words # edit words and pull out variable names - $this->WordValues($word, $arr) # .... # rebuild the string # the string and NvP array are returned by reference # end function ### RuleSetControlValue() - optionally set and return a value in rule set controls # get the current rule set number - $this->RuleSetNumber() # if the variable's name is in the rule set control array # if setting the value # do that # .... # retrieve the value # else # retrieve the value from general controls # .... # return result # end function ### RuleSetNumber() - return the count for the current rule set # the count is the index for the current rule set # end function ### RunXbert() - start processing the first rule set # if csv # open the initial CSV rule set - $this->OpenCsvRuleSet() # else # open the initial SQL rule set - $this->OpenTableRuleSet() # .... # process rule set(s) - $this->ProcessRuleSets() # end function ### SaveFunctionName() - save a function name for a rule set # if the named user function does not exist in the calling script # fatal error - $this->ErrorHandler() # .... # store the new function name - $this->RuleSetControlValue() # end function ### SeekNewNode() - get and optionally set the flag to seek a new level # if we need to seek a new level # if no node is provided, use the current node # set flag to look for a new node in this rule set- $this->RuleSetControlValue() # .... # return a node value when seeking a new node level in this rule set- $this->RuleSetControlValue() # end function ### SetResponse() - set a value in the local <> variable # if numeric # make it floating type # else # test for abort in lower case # if the response is the abort word # send a line to the summary message - $this->SummaryMessage() # end processing gracefully - $this->WrapUp() # .... # .... # set value in local response variable - $this->LocalValue() # end function ### SqlSource() - true when the current source is from an SQL table # return true if the current source is a mySql table - $this->RuleSetControlValue() # end function ### StandardCsvFile() # read the 1st 1K from CSV file into memory - $this->RuleSetControlValue() # if "," is in the data, treat this as a standard CSV file # mark the local control value -$this->RuleSetControlValue() # end function ### SummaryMessage($str) - optionally add to the summary message and return it # if there is a string being added # add where to the string - $this->WhereAmI() # .... # add to and fetch the complete summary message - $this->GetAddToMessage() # return the message # end function ### TableExists() - true when the named database.table exists # assume not found # if a table name string is passed # parse the table name into database and table # if a database name is given # make the 'from' SQL clause # append the 'like' SQL clause # else # make just the 'like' SQL clause # .... # query for the tablename using the 'from' and 'like' patterns # return whether or not a data set is found # .... # .... # true when some rows are found # end function ### TraceMsgToTable() - send a SQL insert to trace table # build an insert SQL using the string passed - $this->RuleSetControlValue() # execute it - $this->ExecuteSql() # end function ### TraceOnToFile() - activate a trace file when there is a valid file name # iif we have previously activated trace in this rule set- $this->RuleSetControlValue() # note that tracing is back on - $this->RuleSetControlValue() # .... # end function ### TraceOnToTable() - open or activate a table for tracing # if no db connection # fatal error # .... # if we have previously activated trace in this rule set- $this->RuleSetControlValue() # note that tracing is back on - $this->RuleSetControlValue() # .... #end function ### UniqueName() - true when the control name is not used in other rule sets # assume it is unique # if there is name to look for - $this->RuleSetControlValue('') # get current rule set - $this->RuleSetNumber() # loop through remaining rule sets - those with lower numbers # back up one rule set # if the name matches # this is not unique to the current rule set # .... # <--- # .... # return result # end function ### ValueForAVariable() - return a value for use with 'GLO' or 'LOC' commands # if something is in part 2 of the text of the current line # use that # else # use the latest local response value - $this->LocalValue() # .... # if numeric # make it floating point # .... # return result # end function ### VariableName() - return a variable name when surrounded by <> # if a word and the word passed has <> # make it lower case # else # return false # .... # return the result # end function ### WhereAmI() - return the rule set and line number not being processed # if there is an open rule set - $this->RuleSetControlValue() # get line number and rule set name - $this->RuleSetControlValue() # build a return message # else # we must be at the initial rule set # .... # return it # end function ### WordValues() - replace any variables in the word and append to an array of variables # default to the word passed and trim it # a single word may be made up of 0, 1 or more variables # if 1 or more variables are in the word - $this->WordVariables() # loop thru the variable array # replace the variable in the word with its value when found # append to the var array passed # <--- # .... # end function ### WordVariables() - return an array of any variable names found in the word with values # a word contains no spaces but may have one or more variables # assume no variable names are found # starting with 1st character of the word # if there is < and > properly positioned in the word # extract the variable name with the <> # save it as lower case # hold it in the return array # get local and global values for a given variable name - $this->LocalValue() $this->GlobalValue() # if there is a value for the variable name - $this->LocalValue($word() # save it in the return array # elseif there is a global value # save it in the return array # .... # look for another variable # .... # return whatever was found for variables # end function ### WrapUp() - done with all processing # if there was a db connection # unlock any tables that may be locked - $this->ExecuteSql() # .... # if there is a 'Finalize' function in the calling script # call it # .... # while there are open rule sets - $this->RuleSetNumber(), # close one - $this->CloseRuleSet() # <--- # exit program # end function ### WriteToDebug() - call the debug user function when it exists # get function name - $this->RuleSetControlValue() # build a message with the name of the rule set and line # - $this->WhereAmI() # call the debug function with the message # end function ### WriteToTrace() - write the trace message to a trace file or table # if trace is not on in this rule set - $this->RuleSetControlValue() # return now # .... # if tracing to a text file - $this->RuleSetControlValue() # build a complete trace message - $this->WhereAmI() # if there is a trace message # build the message with new line # .... # if the write fails # error - $this->ErrorHandler() # .... # .... # if tracing to a mySql table - $this->RuleSetControlValue() # first send a blank line - $this->TraceMsgToTable() # send the line number and rule set name - $this->TraceMsgToTable(), $this->WhereAmI() # if there is a trace message # break the whole message into lines # loop through the parts of the whole message # remove CR when present # write the line to the trace table - $this->TraceMsgToTable() #<--- # .... # .... # end function ### __construct() construct the class # make array of math commands - mathCommands # make array of if - true false test commands - comparisonCommands # make array of 'is set' commands # make array of control commands - controlCommands # commands for user interaction - interactionCommands # make array of commands for opening and closing operations - openCloseCommands # make array of longer 4-char commands # set the date and time this started # end function # end class