NOTE : THIS FRAMEWORK IS NO LONGER MAINTAINED.
It powered several version of the open-source project
that originally developed it, Brain Annex, but that parent project has long
moved on to a python/Flask framework, with a graph database.
cookies
public static $httpsRequired = false; // Flag indicating whether the cookie should be sent over HTTPS only public static $httponly = true; // Flag indicating that the cookie will be made accessible only // through the HTTP protocol - and not thru JavaScript (for security) public static $path = "/"; // The path on the server in which the cookie will be available from; // if set to '/', the cookie will be available within the entire domain
public static function set($name, $value, $expire, $domain = false)
/* Cookie-setting function.
IMPORTANT: it must be called *prior* to outputting any text
ARGUMENTS
$name String with cookie name (cannot be empty)
$value String with cookie value
$expire The time (expressed as a Unix timestamp) when the cookie is set to expire.
If set to 0, it'll expire at the end of the session (when the browser closes)
To avoid having to deal with Unix-timestamp values, more specialized methods, such as setDays(), are also provided in this class.
$domain [OPTIONAL] Site's domain name (such as "example.com"); if missing, the domain is inferred from the server variable "SERVER_NAME"
RETURN VALUE: If output exists prior to calling this function, this function will fail and return FALSE;
if it successfully runs, it will return TRUE. (This does NOT indicate whether the user accepted the cookie.)
*/
public static function setSessionOnly($name, $value, $domain = false)
// Same as set(), but the cookie will expire at the end of the session (when the browser closes)
public static function setDays($name, $value, $expireDays, $domain = false)
// Same as set(), but with the expiration time expressed as in "number of days from now"
public static function setYears($name, $value, $expireYears, $domain = false)
// Same as set(), but with the expiration time expressed as in "number of years from now"
public static function clear($name, $domain = false)
/* Clear the cookie with the given name, in the specified domain (such as "example.com")
IMPORTART: it must be called *prior* to outputting any text.
If the site's domain name (such as "example.com") is missing, it is inferred from the server variable "SERVER_NAME"
RETURN VALUE: If output exists prior to calling this function, this function will fail and return FALSE;
if it successfully runs, it will return TRUE. (This does not indicate whether the user accepted the cookie.)
*/
public static function get($name)
// Return the value of the cookie with the specified name (in the domain of the calling script); if none is found, return false
public static function isCookieSet($name)
// Return true if a cookie by that name is set (in the domain of the calling script)
public static function requireHTTPS($required = true)
// Use this function to require an HTTPS connection (or to turn off such a requirement if previously set)
public static function forceHTTPonly($force = true)
// Use this function to restrict the cookie's access thru the HTTP protocol, rather than thru JavaScript (or to turn off such a requirement if previously set)
public static function setPath($path = "/")
// Use this function to set the path on the server in which the cookie will be available from;
// if using '/', the cookie will be available within the entire domain
dbasePDO
public $dbHandler; // Object created by call to new PDO() public $dbaseServer; // Database server (e.g. "localhost" or "mysql.mysite.com") public $dbase; // Name of default database schema public $dbUser; // Database username public $pass; // Password for the given database user public $errorSummary = ""; // Short version of the last database error, if applicable public $errorDetails = ""; // Longer version of the last database error, if applicable public $dbLogFile = ""; // Optional [VERY RECOMMENDED] file in which to keep log of database errors (re-tries and outright failures)
public function setConnectionParameters($dbaseServer, $dbase, $dbUser, $pass)
// Permits the setting of all 4 connection parameters at once
public function dbaseStart($retry = false)
/* Create a new database connection. Any existing old connection gets cleared (forced restart.)
If successful, it saves the newly-created handle and returns true;
in case of failure, it tries a second time, then it sets some error-reporting properties (plus optional file log) and returns false.
ARGUMENTS
retry flag indicating whether this is the second (and last) attempt
*/
public function selectSQL($sql, $bindings = false)
/* Run the specified SELECT query, with or without bindings.
ARGUMENTS
If the optional argument $bindings is specified, the SQL must contain one or more "?" (without quotes),
and $bindings must contain an array of values, or a single value.
Example1: $sql = "SELECT * FROM mytable WHERE x = ? AND y = ?"
and
$bindings = array($value1, $value2)
A single binding value can also be passed without an array. [i.e. a scalar $s gets converted to array($s)]
Example2: $sql = "SELECT * FROM mytable WHERE x = :p1 AND y = :p2"
and
$bindings = array(':p1' => $value1, ':p2' => $value2))
RETURN a (possibly emtpy) traversable associative/num dataset (aka "PDO statement".)
In case of ERROR, false is returned, and the "errorSummary" and "errorDetails" properties are set; also, the error is logged.
The retry flag in the database object determines if a 2nd attempt is made in case of error.
NOTES
=====
If an ASSOCIATIVE dataset is desired instead of an associative/numeric one, just do:
$result = selectSQL($sql);
$result->setFetchMode(PDO::FETCH_ASSOC);
[note: with an associative array, one cannot use the list() construct]
Likewise, if desiring a purely NUMERIC dataset, do:
$result = selectSQL($sql);
$result->setFetchMode(PDO::FETCH_NUM);
If one wishes to determine how many records were retrieved, use pdo_num_rows() IF using a MySQL database
TYPICAL CALLING CODE:
====================
$sql = "SELECT a, b FROM table";
$result = $dbaseHandler->selectSQL($sql);
EXAMPLES OF HOW TO USE THE RESULT:
=================================
(1)
foreach ($result as $row) {
$myFieldValue = $row["myFieldName"]; // An associative/numeric dataset is returned by default (see notes above.) Note that $row[0], etc., will also work
echo "myFieldValue: $myFieldValue<br>";
}
(2)
while($row = $result->fetch())
echo $row['field1']; // $row[0], etc., will also work
(3)
while($row = $result->fetch(PDO::FETCH_ASSOC)) // The PDO::FETCH_ASSOC part is not strictly necessary, because the default is PDO::FETCH_BOTH
echo $row['field1'];
(4)
while($row = $result->fetch(PDO::FETCH_NUM)) // The PDO::FETCH_NUM part is not strictly necessary, because the default is PDO::FETCH_BOTH
echo $row[0];
(5)
$resultArray = $result->fetchall(); // Transform the result into an array (containing all of the result-set rows);
// for the individual entry (i.e. each dbase record) if one wishes a purely associative array, use fetchall(PDO::FETCH_ASSOC);
// for a purely numeric arrays, use fetchall(PDO::FETCH_NUM)
// An empty array is returned if there are zero results to fetch, or FALSE on failure
// For convenience, the methods selectSQLarrayNum() and selectSQLarrayAssoc() are also available
foreach($resultArray as $row) {
... // like before
}
(6)
$sql = "SELECT a, b FROM table";
$result = $dbObject->selectSQL($sql);
foreach ($result as $row) {
list($a, $b) = $row;
echo "a: $a | b: $b<br>";
}
(7)
$sql = "SELECT a FROM table";
$result = $dbObject->selectSQL($sql);
$resultArray = $result->fetchall(PDO::FETCH_COLUMN); // An array with all the returned entries from the single column
(8)
$sql = "SELECT f0, f1, f2, f3 FROM table";
$result = $dbObject->selectSQL($sql);
while(($columnValue = $result->fetchcolumn(2)) !== false) // Do NOT use on Boolean fields. See http://php.net/manual/en/pdostatement.fetchcolumn.php
echo $columnValue;
// fetchColumn returns a single column from the next row of a result set
(9)
Use fetch(PDO::FETCH_CLASS) or fetch(PDO::FETCH_INTO) to save the results into objects
*/
public function selectSQLarrayNum($sql, $bindings = false)
/* Shortcut to invoking selectSQL() and then converting the entries of the returned dataset into a numeric array.
An empty array is returned if there are zero results to fetch, or FALSE on failure.
Example of return value:
Array(
Array ( [0] => 123 [1] => 222)
Array ( [0] => 666 [1] => 333)
}
Example of usage:
$resultArray = selectSQLarrayNum($sql);
foreach($resultArray as $row) {
echo $row[0] . "<br>";
}
*/
public function selectSQLarrayAssoc($sql, $bindings = false)
/* Shortcut to invoking selectSQL() and then converting the entries of the returned dataset into an associative array.
An empty array is returned if there are zero results to fetch, or FALSE on failure.
Example of return value:
Array(
Array ( [field1] => 123 [field2] => 222)
Array ( [field1] => 666 [field2] => 333)
}
Example of usage:
$resultArray = selectSQLarrayNum($sql);
foreach($resultArray as $row) {
echo $row["field1"] . "<br>";
}
*/
public function selectSQLFirstRecord($sql, $bindings = false)
/* Run the specified SQL SELECT statement, and return the first row of the result (as an assoc/num array.)
A "LIMIT 1" statement is automatically added to the SQL. WARNING: make sure that the $sql passed to it does not already have "LIMIT" statement; if it does, use the method selectSQLOneRecord() instead
In case of no records, return null.
In case of error, return false.
Typically used with SELECT statements that return a single record.
Note: pdo_num_rows() cannot be used after this function call,
because the PDOstatement is not returned (maybe it could be saved in the object?)
EXAMPLE 1:
$sql = "SELECT a, b, c FROM myTable";
list($a, $b, $c) = $db->selectSQLFirstRecord($sql);
EXAMPLE 2:
$sql = "SELECT * FROM myTable";
$resultRow = $db->selectSQLFirstRecord($sql);
$a = $resultRow["a"];
$b = $resultRow["b"];
$c = $resultRow["c"];
*/
public function selectSQLOneRecord($sql, $bindings = false)
/* Run the specified SQL SELECT statement, and return the first row of the result (as an assoc/num array.)
In case of no records, return null.
In case of error, return false.
Typically used with SELECT statements that return a single record.
Note: pdo_num_rows() cannot be used after this function call,
because the PDOstatement is not returned (maybe it could be saved in the object?)
EXAMPLE 1:
$sql = "SELECT a, b, c FROM myTable ORDER BY a LIMIT 1";
list($a, $b, $c) = $db->selectSQLFirstRecord($sql);
EXAMPLE 2:
$sql = "SELECT * FROM myTable";
$resultRow = $db->selectSQLFirstRecord($sql);
$a = $resultRow["a"];
$b = $resultRow["b"];
$c = $resultRow["c"];
*/
public function selectSQLOneValue($sql, $bindings = false)
/* Run the specifed SQL query (expected to be a SELECT statement.)
RETURN the first (zero-th) column of the first (zero-th) row.
In case of no records or error, return null.
The returned value might be a null if that's what contained in the database or returned by a database function in the SELECT statement.
Typically used with SELECT statements that return a single value, such as a count(*)
In case of error, return false.
EXAMPLE:
$myCount = selectSQLOneValue("SELECT count(*) FROM myTable WHERE ID > 7");
IMPORTANT NOTE: Aggregate functions sometimes return null. For example, "SELECT max(ID) myTable WHERE ID > 100" will return null if no records satisfy the condition;
the calling function may test for this with the "===" identity check, as follows :
if ($result === null) // to distinguish no records found vs. a returned value of zero
Note: pdo_num_rows() cannot be used after this function call,
because the PDOstatement is not returned (maybe it could be saved in the object?)
*/
public function selectSQLOneColumn($sql, $columnPosition, $bindings = false)
/* Return a numeric array with the values of the specified column from the result of the SQL query;
in case of error, return false.
Column positions are counted from zero (i.e., 0 corresponds to the first column)
WARNING: do NOT use on Boolean fields. See http://php.net/manual/en/pdostatement.fetchcolumn.php
*/
public function isInTable($table, $field, $value)
/* Return true if the given value is found at least once (in any row) in the specified field in the given table;
false, otherwise
EXAMPLE:
isInTable("myTable", "field1", 123)
*/
public function pdo_num_rows($PDOstatement)
/* This function emulates mysql_num_rows(), but only works for MySQL databases.
1) It returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the specified PDOStatement object.
2) If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement.
However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.
(See http://php.net/manual/en/pdostatement.rowcount.php and http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers)
In case of error, it return false
*/
public function pdo_result($PDOstatement, $rowCount, $field)
/* Emulates mysql_result()
*/
public function countRecords($table, $selectSubquery = "")
/* Return the number of records in the given table when the specified subquery (the part *after* the WHERE in the sql query) is applied;
if the subquery is a blank string or missing, then the total number of records in the table is returned.
In case of error, return false. A === false check on the return value should be done to distinguish zero records from an error condition.
EXAMPLE: $number_of_records = countRecords("myTable" , "`ID` > `0");
*/
public function modificationSQL($sql, $bindings = false)
/* Run the specified "modification" SQL (i.e. UPDATE, INSERT, DELETE , CREATE TABLE , or DROP TABLE.)
If the optional argument $bindings is specified, the SQL must contain one or more "?" (without quotes),
and $bindings must contain an array of values, or a single value.
Example1: $sql = "UPDATE myTable SET field = ? WHERE ID = ?";
and
$bindings = array($value1, $value2);
A single binding value can also be passed without an array. [i.e. a scalar $s gets converted to array($s)]
Example2: $sql = "UPDATE myTable SET x = :p1 AND y = :p2";
and
$bindings = array(":p1" => $value1, ":p2" => $value2);
Example3: $sql = "INSERT INTO myTable (`col1`, `col2`)
VALUES (:username, :email)";
and
$bindings = array(":username" => "me myself", ":email" => "a@b.com");
RETURN VALUE
In case of successful insert, update or delete operations, return the number of affected rows
In case of successful create/drop table operations, return 0
In case of error, -1 is returned, error messages are logged, and some error properties get set
*/
public function retrieveInsertID()
/* Return the auto-increment value from the last INSERT operation
*/
public function createTable($tableName, $columnDefinitions, $primaryKey, $otherKeys = false, $tableOptions = "ENGINE=InnoDB , DEFAULT CHARSET=latin1")
/* Create a mySQL table.
If successful, RETURN true; otherwise, return false and set error properties.
See https://dev.mysql.com/doc/refman/5.5/en/create-table.html
EXAMPLES of arguments:
$tableName "myTable"
$columnDefinitions "`ID` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`field1` varchar(16) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`field2` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
`field3` smallint(5) unsigned DEFAULT NULL,
`field4` text CHARACTER SET latin1 COLLATE latin1_general_ci,
`timeStamp` timestamp DEFAULT CURRENT_TIMESTAMP
$primaryKey "`field1`, `field2`"
$otherKeys [OPTIONAL] "UNIQUE KEY `someNameForKey` (`field3`,`field4`)"
"KEY `sessionID` (`sessionID`), KEY `subID` (`subID`)"
$tableOptions [OPTIONAL] "ENGINE=MyISAM , DEFAULT CHARSET=latin1", AUTO_INCREMENT=69
*/
public function getFieldNames($tableName)
/* Return an array of field names in the given table (or false in case of failure).
Only tested on MySQL.
ARGUMENT:
$tableName A string with the table's name. Example: "myTable" (do not wrap the name in database back quotes)
*/
public function tableExists($table)
// Return true iff the specified table already exists in the current database
public function allTables()
// Return a numeric array with the names of all the tables in the current database, or false in case of error
public function extractForeignKeys($tableName)
/* Look up and return all the foreign keys of the given table.
This function may only work on MySQL.
RETURN: a numeric array whose entries are associative arrays of the form [columnName, foreignTable, foreignColumn]
If no foreign keys are present, an empty array is returned, or false in case of error.
Example:
Array ( [0] => Array ( [columnName] => customerID [foreignTable] => customers [foreignColumn] => ID )
[1] => Array ( [columnName] => shipperID [foreignTable] => shippers [foreignColumn] => ID )
)
*/
public function mapFieldsToForeignKeys($tableName)
/*
RETURN:
If no foreign keys are present, an empty array is returned, or false in case of error.
Example:
Array( "customerID" => Array( [0] =>"customers" , [1] => "ID") ,
"shipperID" => Array( [0] =>"shippers" , [1] => "ID")
)
*/
/*************************************
DEBUGGING METHODS
*************************************/
public function debugPrintSQL($sql, $bindings, $expandSQL = true)
/* Return an HTML-formatted string with the SQL and the bindings.
If requested, reconstruct and print out the full SQL (typically for debugging purposes) after expanding the bindings.
*/
public function debugPrintTextSQL($sql, $bindings, $expandSQL = true)
/* Return a plain-text string the SQL and the bindings.
If requested, reconstruct and print out the full SQL (typically for debugging purposes) after expanding the bindings.
*/
public function debugLogSQL($sql, $bindings, $expandSQL = true)
/* Write to the log file a plain-text string the SQL and the bindings.
If requested, reconstruct and log the full SQL (typically for debugging purposes) after expanding the bindings.
*/
directedGraphs
public $methodLog = ""; // Optional descriptive text log of the actions (typically modifications) // done by an individual class method. Each method that uses this, will first reset it public $methodErrors = ""; // Optional descriptive text that some methods use to pass error info to their calling code // Each method that uses this, will first reset it public $methodResults = false; // Optional variable that some methods use to pass additional info to their calling code // Each method that uses this, will first reset it public $MAX_DEPTH = 50; // Large enough so that anything bigger is likely a runaway recursion public $CATEGORY_ROOT_NODE = 1; // Not recommended to use 0 because nodeID's are autoincrement fields, and MySQL can cause problems when 0 is stored in an autoincrement field
public function __construct($db, $nodesTable, $edgesTable, $traversalTable = "")
/* It's ok if the specified tables don't yet exist in the database.
ARGUMENTS:
db Object of class "dbasePDO"
*/
public function setSQLclause($clause)
/* Set an optional clause to insert in all the lookup statements (e.g., the calling module might use this to enforce restrictions, permissions, etc.)
*/
public function createNodesTable($semanticsDefs)
/* Create a database table for the nodes, only if it does not already exist.
Each node can carry a set of user-defined "semantics", with the their definitions
Example: $semanticsDefs = array("name" => "varchar(60) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL",
"remarks" => "varchar(100) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL");
Return 0 if no errors (table may or may not be created) and -1 in case of errors.
Note: the table's autoincrement field starts at 1; there is no way (without access to the mysql server) to make it start at 0
*/
public function createEdgesTable($semanticsDefs)
/* Create a database table for the edges, only if it does not already exist.
Each edge can carry a set of user-defined "semantics", with the their definitions
Example: $semanticsDefs = array("childSequenceNo" => "smallint(5) unsigned DEFAULT '0' COMMENT 'used to list a node s children in a particular order'");
Return 0 if no errors (table may or may not be created) and -1 in case of errors
*/
public function createTraversalTable()
/* Create a database table for the saved graph traversal, only if it does not already exist.
Return 0 if no errors (table may or may not be created) and -1 in case of errors
*/
public function setNodeAsRoot($nodeID)
/* Mark the specified node as the ROOT by setting its ID to a pre-specified value (such as 1) stored in property CATEGORY_ROOT_NODE
In case of failure, return an error message; if successful, a blank string is returned
*/
public function addChild($parentID, $semantics = false)
/* Add a Node with a child relationship to the specified parent; optionally populate the newly-added node with the specified semantics
If the optional $semantics argument is present, it is expected to be an array of [fieldName => fieldValue]
elements to be added in the table of nodes for the newly-created record. Example: ["name" => "myName" , "numField" => 123] Do NOT place SQL quotes around the names!
The property methodLog records the actions taken by this method (except error conditions.)
RETURN VALUE: In case of failure, return an error message;
if successful, a blank string is returned, the "methodResults" property is set to the AutoIncrement value for new node ID,
and the "methodLog" property is set to details of the process
*/
public function addEdge($parentID, $childID)
/* Add a directed edge between the specified nodes (from parent to child.)
In case of failure, return an error message, and possibly set the "errorSummary" and "errorDetails" properties;
if successful, a blank string is returned
*/
public function removeEdge($parentID, $childID)
/* Remove the edge between the specified nodes.
IMPORTANT: No check is done about the child node possibly becoming orphaned as a result. The calling function might opt to use the method numberParents() to determine if the node is an only child.
In case of failure, return an error message, and possibly set the "errorSummary" and "errorDetails" properties;
if successful, return a blank string.
*/
public function switchParentNode($childID, $oldParentID, $newParentID)
/* Switch a parent/child relationship between the specified nodes.
Take the child away from the old parent, and re-assign to the new one.
In case of failure, return an error message, and possibly set the "errorSummary" and "errorDetails" properties;
if successful, a blank string is returned
*/
public function switchChildNode($parentID, $oldChildID, $newChildID)
/* Switch a parent/child relationship between the specified nodes.
From the parent, take the old child away , and relace it with the new child.
In case of failure, return an error message, and possibly set the "errorSummary" and "errorDetails" properties;
if successful, a blank string is returned
*/
public function deleteNode($nodeID)
/* Delete the specified node in the graph, including all the graph edges entering or leaving the node.
(The caller function ought to first to determine whether ok to delete the node. Also, no check is done to prevent deletion of the root)
In case of failure, return an error message, and possibly set the "errorSummary" and "errorDetails" properties;
if successful, a blank string is returned
*/
public function retrieveNodes($clause = "", $sortOrder = "")
/* Locate all the nodes, subject to the optional restriction, expressed as an SQL subquery clause: for example "not isnull(`remarks`)" [Note: this is above and beyond
the optional SQL clause specified in the class property "SQLclause"]
If a sort order is specified, it must be of a form suitable for a SQL query (example1: "`name`" ; example2: "`name`, `ID` DESC")
RETURN a traversable data set of nodes, sorted by the specified field. Each entry is an array of 1 or more parts: ID and any present semantic fields (for example [ID, name, remarks, permissions])
In case of error, return false.
*/
public function nodeSemanticsMatches($fieldName, $fieldValue)
/* Locate all the nodes whose value in the specified semantic field matches the given string (as a substring.)
RETURN an array of matches, sorted by the specified semantic field; each entry is an associative/enum array of at least 2 parts: [node ID, fieldName, any_other_semantic_fields].
In case of error, return false.
This function is useful for searches.
*/
public function semanticsLookupSingleField($nodeID, $fieldName)
/* Look up and return the value of the specified semantic field for the given node.
Example: semanticsLookupSingleField(123, "`name`")
A blank string is returned if no semantics are found, or in case of error.
*/
public function semanticsLookup($nodeID, $fields = "*")
/* Look up and return the semantics data of the specified node from its ID, as an array $fieldName => $fieldValue
A blank string is returned if no semantics are found, or in case of error.
*/
public function updateNodeSemantics($nodeID, $newSemantics)
/* Change the semantic values attached to the specified node, as directed.
Example: $newSemantics = array("name" => "someName" , "remarks" => "someRemarks" , "permissions" => "somePermissions")
RETURN VALUE: 1 if the record was modified, 0 if not (0 indicates an attempt to update to the same values that the record already had.)
In case of failure, an exception is thrown.
*/
public function fetchChildren($nodeID, $sortby = false)
/* Fetch all the children of the given node, optionally sorted as requested, within the constraints of the optional "SQLclause" property.
Return a (possibly empty) an associative/enum array of elements that contain: [childID, <all the semantic fields>]
In case of failure, false is returned.
Note: it's convenient to have an array as a result, because it can be rewound if desired, with the reset() function.
*/
public function fetchParents($nodeID, $sortby = false)
/* Fetch all the parents of the given node, optionally sorted as requested, within the constraints of the optional "SQLclause" property.
Return a (possibly empty) associative/enum array of elements; each of them is an array that contains: [parentID, <all the semantic fields>]
In case of failure, false is returned.
Note: it's convenient to have an array as a result, because it can be rewound if desired, with the reset() function.
*/
ADDED IN v. 4.2
public function numberParents($nodeID)
/* Return the number of parents of the given node, within the constraints of the optional "SQLclause" object property.
In case of failure, false is returned.
*/
public function fetchSiblings($nodeID, $sortby = false)
/* Fetch all the siblings of the given node, optionally sorted as requested, within the constraints of the optional SQL WHERE clause.
Return a (possibly empty) associative/enum array of elements, each of whis is an associative array that contains: [ID, name, remarks].
In case of failure, false is returned.
Note: it's convenient to have an array as a result, because it can be rewound if desired, with the reset() function.
*/
function locateDanglingEdges()
// Locate all the edges (ideally none) that lack a corresponding node at either (child or parent) end.
// Return an array of all such edges
function traverseGraphAndSave($sortby)
/* Traverse the graph from its root, and build (or rebuild) the array of graph-traversal information,
stored as the "graphTraversal" property, and saved in the table whose name is stored in the property "traversalTable".
Traversal is done in pre-order (node first, then the traversals of its children in turn.)
RETURN VALUE: an empty string if successful, or an error message in case of errors.
*/
function traverseGraph($sortby)
/* Traverse the graph from its root, and build (or rebuild) the array of graph-traversal information,
stored as the "graphTraversal" property.
Traversal is done in pre-order (node first, then the traversals of its children in turn.)
RETURN VALUE: an empty string if successful, or an error message in case of errors.
*/
function recursiveGraphTraversalInMemory($nodeID, $depth)
/* Recursively traverse the directed graph, starting at the specified node, and update accordingly the array
in the "graphTraversal" property, adding to it info about the specified node (its ID and depth) and, recursively, about its children.
Information about the node's children is expected to be in memory, stored in the property "edgeArray".
To catch possible runaway recursions, the recursion is aborted if a depth of $MAX_DEPTH is reached.
ARGUMENTS
nodeID: starting point
depth: depth (in the graph traversal) of the starting-point node
RETURN VALUE: an empty string if successful, or an error message in case of errors.
SIDE EFFECTS: update the array in the "graphTraversal" property
*/
formBuilder
public $paneTitleStyle; // If provided, it will be used for each pane (containing a form) public $tdWrapOptions; // Options for theelement of each cell (containing a form) public $nCols; // Number of columns in the table of forms. This attribute needs to be public, because in some cases // (involving table cells spanning multiple rows) it must be modified during execution public $tableOptions = "border='1'"; // Styling for the larger table private $row = 1; // Current row in the construction of the Control Panel private $col = 1; // Current column private $CPhtml = ""; // HTML forming the Control Panel
controlPanel – Public Methods
function __construct($nCols) // CONSTRUCTOR
// Specify the number of columns in the table of forms
public function addForm($form)
// Add a form (an object of type "formBuilder") to the Control Panel being built
public function addGeneralPane($html)
// Add a general pane (HTML consisting of <td> element and its content) to the Control Panel being built
public function generateCP()
// Generate and return the Control Panel
formBuilder – Public Properties
public $title; // Optional title to display just before the <form> element public $titleStyle = "font-weight:bold; color:brown; font-size:12px"; // Default style to be used for the title public $formStyle; // CSS used on the form public $handler; // URL of script that will handle the form public $submitLabel = "Enter"; // Text to show on the Submit button public $hiddenOptions; // Array of HTML attributes for an optional <hidden> control. E.g. // array("name='group' value='vert'" , // "name='plugin' value='t'") // If just 1 element, it may optionally be passed as a string instead
formBuilder – Public Methods
function __construct($title, $handlerScript, $method = "POST") { // CONSTRUCTOR
/* $title Optional title (header text) to display just before the <form> element
$handlerScript URL of script that will handle the form. Use NULL if employing a JS handler
$method; Method ("POST" or "GET") to be used by the form to invoke its handler script
*/
public function addJS_handler($func)
// Specify a JS handler function, invoked when the form's submit button is clicked;
// it will be the JS handler's responsibility, if desired, to submit the form with document.testform.submit()
// An object containing the form is passed as argument to the JS function; for example, the function can extract the value of a field as: formObject.someFieldName.value
// See https://www.javaworld.com/article/2077176/using-javascript-and-forms.html?page=2
public function addHeaderLine($labelString)
// Append a row with the specififed label and a blank control. This pair might be used as a header at the top of the form, or as a mid-form sub-header/separator text
public function addFooter($text)
// Insert the given text (possibly HTML) at the very bottom of the form, right after the Submit button
public function addBlankLine()
// Append a row with a blank label and a blank control
public function addControl_General($labelString, $controlHTML)
/* Append a row with the specififed label and a general control (its HTML text).
Example of a text-input control: "<input type='text' name='someName'>"
More specific methods in this class are provided for many common controls (such as text, pulldown menus, etc)
*/
public function addControlText($label, $controlName, $size = null)
// Append a row with the specififed label and a text control, optionally specifying the size of the text field
public function addControl_Text($label, $controlName, $parArray = false)
/* Append a row with the specified label, and a text-input control with the given name and parameters.
The optional parameters of the text-input control are specified using entries of the $parArray argument; allowed values:
"value"
"size"
"maxlength"
"id"
"onclick"
"onchange"
EXAMPLE: $parArray = array("size" => "3" , "maxlength" => 5, "value" => "123" , "onclick" => "myClickFunction()")
For now, a few JS events are handled on a case-by-case.
Later on, maybe turn them into an array such as "js" => array("onclick" => "myClickFunction()" , "onchange" => "myChangeFunction()")
OR maybe: "js" => "onclick='myClickFunction()' onchange='myChangeFunction()'"
*/
public function addControl_Checkbox($label, $controlName, $parArray = false)
/* Append a row with the specififed label, and a chebox control with the given name and parameters.
EXAMPLE: addControl_Checkbox("Gift wrapped?", "gift", array("checked" => true))
Optional parameters are specified using elements of the $parArray argument; allowed values:
"checked"
"onclick"
"onchange"
EXAMPLE: $parArray = array("size" => "3" , "maxlength" => 5, "value" => "123" , "onclick" => "myClickFunction()")
For now, a few JS events are handled on a case-by-case.
Later on, maybe turn them into an array such as "js" => array("onclick" => "myClickFunction()" , "onchange" => "myChangeFunction()")
OR maybe: "js" => "onclick='myClickFunction()' onchange='myChangeFunction()'"
*/
public function addControl_Password($label, $controlName, $parArray = false)
/* Append a row with the specififed label, and a password-input control.
Optional parameters are specified using elements of the $parArray argument; allowed values:
"size"
"maxlength"
"id"
"onclick"
"onchange"
EXAMPLE: $parArray = array("size" => "3" , "maxlength" => 5, "value" => "123" , "onclick" => "myClickFunction()")
For now, a few JS events are handled on a case-by-case.
Later on, maybe turn them into an array such as "js" => array("onclick" => "myClickFunction()" , "onchange" => "myChangeFunction()")
OR maybe: "js" => "onclick='myClickFunction()' onchange='myChangeFunction()'"
*/
public function addControl_PulldownMenu($label, $menuDataset, $controlName, $selectedInstructions = "[SELECT]", $optionsArray = false)
/* Append a row with the specififed label, and a pull-down menu.
ARGUMENTS:
$menuDataset Must be a traversable object; for example an array or something returned by an SQL select query; its entries may be single values or arrays.
If its entries are single values or arrays with 1 element, they are used for both the picked value and the shown value;
if they are arrays with 2+ elements, the 1st element is used for the picked value, and the 2nd one is used for the shown value.
$controlName A string representing an array. It MUST end in "[]"; for example, "someName[]"
$selectedInstructions Optional. If present (and not over-ridden, see below), the first entry of the menu shows the specified string and is selected; its picked value is blanl
$optionsArray Optional array.
Element "style", if present, sets a CSS style for the <select> tag
Element "selected", if present, specifies a value in the dataset that will selected. This will only occur if the specified value actually occurs in the dataset; if it occurs, it will override $selectedInstructions, if applicable
*/
public function addControl_Hidden($name, $value)
// Add a "hidden input" to the form, with the given name/value pair
{
$this->hiddenOptions[] = "name='$name' value='$value'";
}
public function generateHTML($submitLabel = "")
/* Create and return an HTML form (preceded by an optional title, specified at object-instantiation time.)
The optional argument, if present, conveniently allows specifying a label insude the form' SUBMIT button.
*/logging
Class to log SYSTEM MESSAGES (such as alerts or diagnostics) into a text file or an HTML file
DEPENDENCIES: None
EXAMPLE OF USAGE:
$myLog = new logging("myLogFile.htm");
$myLog->insertSeparator();
$myLog->fileLogging("Operation started");
$myLog->logMessage(1, "some detail", "font-weight:bold");
$myLog->logErrorMessage("Operation FAILED");
Configurable Public Properties
public $logFile; // Name of file where to store the log messages. If file already exists, the message gets appended; otherwise, the file gets created public $htmlLogging = true; // Flag indicating whether the log file is meant to be an HTML file; if false, it will be regarded as a text file public $style = ""; // Optional HTML styling to apply to the message; if present, a tag is used public $indentAmount = 0; // 0 indicates no indentation; higher integers represent progressively-increasing indentation // (in points for HTML messages, or number of blanks for text ones) public $indentIncrementHTML = 15; // Indent amount , relative to the previous message "level", in points (for HTML messages only) public $indentIncrementText = 3; // Indent amount , relative to the previous message "level", in characters (for text messages only) public $timeStamp = true; // Whether to timestamp the message public $extraPrefix = ""; // Optional extra prefix (at the very beginning) public $separator = "_____________________________________________________________________________________________________________________";
Public Methods
function __construct($logFilename)
// Specify a filename to use for logging; it will generally be a .txt for or .htm filed depending on the "htmlLogging" property
public function logMessage($level, $msg, $style = "")
/* HIGH-level function to log the given text message to the designated log file (whose name is stored in the class property "logFile"). If the log file doesn't exist, it gets created.
A new line is added after each entry.
ARGUMENTS:
level An integer indicating the "importance": 0 (most important), 1, 2, ...
msg The message to log
style Optional HTML styling to apply to the message, using a <span> tag. Do NOT include the <span> tag
RETURN VALUE: true if successful, or false if not.
*/
public function logErrorMessage($msg)
/* Log an error message. Same as regular logging, but with more emphasis, and the backtrace of the call stack
*/
public function insertSeparator()
// Append the standard separator (and a new line) to the log file
public function fileLogging($msg)
/* LOW-level function to append the given message to the log file (or to create a log file, if not present.)
A newline is automatically added.
If htmlLogging is enabled, then any HTML in the message (which could mess up the log file's viewing) is protected with htmlentities
The message is optionally modified by:
- the string: "date time", where date is mm/dd/yy
- an indentation
- a user-specified prefix
- a user-specifief HTML style
RETURN VALUE: true if successful, or false if not.
*/parameterValidation
Class with static functions for PARAMETER VALIDATION (for example, for site security)
DEPENDENCIES: None
USAGE EXAMPLE:
parameterValidation::validateInteger($myValue)
Public Methods
public static function validatePositiveInteger($arg)
/* Check if the given argument is a positive, non-zero integer, (or a string representing such an integer.)
Return true if the check succeeded, or false if not
*/
public static function validateInteger($arg)
/* Check if the given argument is a positive integer (or a string representing such an integer.)
Return true if the check succeeded, or false if not
*/
public static function validateNumeric($arg)
/* Check if the given argument is a numeric quantity.
Return true if the check succeeded, or false if not
*/
public static function validateAlphanumeric($arg)
/* Verify that the given argument is alphanumeric
*/
public static function validateAlphanumericOrBlank($arg)
/* Verify that the given argument is alphanumeric or blank
*/
public static function validateAlphanumericOrUnderscore($arg)
/* Verify that the given argument is alphanumeric or underscore
*/
public static function validateUsername($user)
/* Verify that the given username (which might also be an email address or an ID) is drawn from the allowed character set
*/
public static function validatePassword($pass)
/* Verify that the given password is drawn from the allowed character set
*/
public static function validatePattern($string, $pattern, $debug = false)
/* Check the given string value against the specified RegEx pattern.
If it matches (or if it's an empty string), return true; otherwise print out the given error message (details based on the $stage flag) and return false.
EXAMPLE: validatePattern($ID, "/^[0-9]+$/", "ID must be an integer. Value provided: “$ID”", true);
*/siteAuth
Class for SITE USER AUTHENTICATION, incl. login/logout management.
Multiple independent websites are supported.
Authenticated user access is implemented by using randomly-generated session ID's, stored in a cookie on the client side,
and in the database on the server side.
The central entity for site authentication is the "User Access ID", which can be thought of as a particular "subscription" (account) of a user to a site.
A particular user might have multiple subscriptions (accounts) on different sites, and even multiple ones on a single site.
The "User Access ID" resolves the particular subscription (account) on a particular site.On the server side, it makes use of 1 database table:
- A login table (specific to this module), to store the sessionID's
DEPENDENCIES:
- dbasePDO This in turn, depends on the "logging" class
- cookies Static methods to set, clear, read and check cookies
- logging Use to log messages (diagnostics, etc) on the serverPublic Properties
public $loginTable = "entry"; // Name of table used to store the session info public $db; // An object of the "dbasePDO" class, to interact with the database public $cookieSession = "pforce"; // Name of cookie with the sessionID public $cookieUsername = "username"; // Name of cookie used to store the user name public $cookieOld = "old"; // Name of cookie used to store the first few digits of the old session ID public $userAccessID = false; // ID that identifies an instance of user access; typically, either a User ID or a Subscription ID. If not logged it, then false public $errorMessage = ""; // It gets set with an explanatory string in case of errors public $messageLogger; // An object of the "logging" class public $allowMultipleLogins = true; // Flag indicating whether the same user account is allowed to be accessed through multiple simultaneous logins public $maxLoginHours = 9; // Login expiration time, in hours. Only applicable if allowMultipleLogins is false public $loginURL; // URL to re-direct to if login is required but user isn't logged in
Public Methods
function __construct($db, $messageLogger) // CONSTRUCTOR
/* $db: An object of the "dbasePDO" class, to interact with the database
$messageLogger: An object of the "logging" class, to log system messages (such as alerts or diagnostics) into a text file or an HTML file
*/
public function userLoginStatusUnverified()
/* Perform a *minimal* check of whether a user is logged in; only to be used in situations where restricted access isn't important.
RETURN VALUE: if logged in, return the username; otherwise, set the property "errorMessage" with an explanation of the failed login, and return FALSE.
The session ID found in the user's cookie is NOT verified against the database.
This function is a "light" version of userLoginStatus(), suitable for low-protection pages.
Since no cookies are set, this function can be invoked anywhere;
this is handy in situations where the page header has already been sent.
*/
public function userLoginStatus()
/* Perform a THOROUGH CHECK of the user's login status, based on the available session cookie value, checked against the database.
IMPORTANT: this function MUST be invoked PRIOR to outputting any text, since it might set cookies.
RETURN VALUE:
If logged in, set the property "userAccessID", and also return that value.
If not logged in, then completely log out user (clear his session variables, cookies, database info),
set the property "errorMessage" with an explanation of the failed login, and return FALSE.
*/
public function validateLoginAndLookupUser()
/* If user's login is validated, return the userAccessID (aka subscriptionID); otherwise, return false
*/
public function loginUser($userAccessID, $username, $stayLogged, $visitorID = false)
/* IMPORTANT: This function, because it may set cookies, must be called before any HTML output is started!
Log in the user with the given ID
ARGUMENTS
$userAccessID Typically, a user ID or a subscription ID
$username Redundant: passed for avoiding an extra database call
$stayLogged Flag indicates whether the login is meant to be persistent upon closing the browser
$visitorID Optional integer, for example to associate a login account with data collected from user prior to login
A database connection is assumed.
For security purposes, a new sessionID is always generated.
The session info is stored in the database table $this->loginTable
COOKIES
$this->cookieSession: the session ID (also stored in the database)
$this->cookieUsername: for convenient access by low-value pages.
$this->cookieOld: the first few digits of the last sessionID, to clear old session values that may still linger around.
This is useful for garbage-collecting of one-time logins in cases
where the user closes the browser w/o logging out.
RETURN VALUES
If successful, return true; otherwise, false. If errors arose, they are noted in the property "errorMessage", and also logged
on server
*/
public function logout($all = false)
/* IMPORTANT: This function, because it may set cookies, must be called before any HTML output is started
If the $all flag is set, then ALL login sessions by the current user (e.g. on different devices or browsers) are terminated.
The username is returned (handy to greet goodbye to the user.)
*/
public function enforceLogin($loginPage = false)
/* Re-direct user to login page if not logged in. Perform a THOROUGH CHECK for the login status.
If user is logged in, return the userAccessID.
The database connection gets started if needed.
[Note: if using the "pageBuilder" module, this function is only used for pages that don't employ the startPage() function]
*/
public function redirectToLoginPage($loginPage)
/* Redirect user to the specified login page, and exit.
A full URL is recommended over a relative path, to cover situations where the request comes from any pages in the site.
The loginPage may not include a # fragment
*/
public function loginCount($userAccessID)
/* Return the number of logins that the user has done.
Handy to optionally pass a login count to the login splash page
*/
public function setupDatabase()
/* Create the table used for managing the logins. To be used for installing a new site.
In case of failure, an exception is thrown
*/
siteMembership
Class for the management of generic user accounts on a site: the underlying database table could have more fields, used by other more site-specific modules
Based on the entity "User-access ID" : identifying a particular membership for some user
DEPENDENCIES:
- dbasePDOPublic Properties
public $errorSummary; // Summary of the last error, if applicable
Public Methods
public function __construct($dbPDO)
// Argument: Object for database operations
public function validateUser($dbField, $usernameOrEmail, $attemptedPassword, $marketingSlogan = "")
/* Attempt to retrieve the membership record for the user with the provided credentials.
Verify that the credentials are valid and that the account is active.
ARGUMENTS:
$dbField Should be the string "email" or "username"
$usernameOrEmail
$attemptedPassword
$marketingSlogan An optional string to be returned to the user in case the memberhips has expirerd
RETURN:
If successful, return the array [ID, username] ; otherwise, return false and set the property "errorSummary" to the reason for failure
*/
public function reValidatePassword($userID, $attemptedPassword)
/* For safety, wheneven a user attempts to make important account changes.
Return true if re-validation suceeeds, or false otherwise.
*/
public function changePassword($userID, $newPass)
// Update the given user's password with a hashed version of the given new one
public function listActiveUsers($siteID)
/* Return a traversable dataset of ACTIVE users on the specified site, with all the available fields in the database table.
Example of a way to traverse it:
$allUsers = $siteMembership_object->listActiveUsers($siteID);
foreach ($allUsers as $user) {
print_r($user);
echo "<br><br>";
}
*/
public function addUserToAccount($siteID, $username, $email, $pass, $permissions = 0)
/* Add a new user to the current site.
Return the new userID if all operations were successful, or false othewise
*/
public function addNewAccount($name, $email, $pass, $newSiteID = null)
/* Add a new account to the site, including a user who is an admin for that account.
Optionally, accept a value for the new siteID (typically used for sites identified by text codes, such as "e", "s");
if a value isn't provided, the next available table record ID (for example 23) is used (as a string; for example "23").
Return the new account ID if all operations were successful, or -1 othewise; in case of errors, also set the property "errorSummary"
*/
public function makeAdmin($userID, $siteManager = false)
/* Mark the specified user as an admin for their account - and, optionally, also as a Site Manager
Return true iff successful
*/
public function setupDatabase()
/* Create the table used for the membership information. To be used for installing a new site.
In case of failure, an exception is thrown
*/
public function alreadyInstalled()
// Return true iff this module table is already installed
public function lookupUserByEmail($email)
// Return the full record of the user with the given email address. If that email occurs in more than one place, the user with the largest ID is picked
public function verifyUserCredentialsAndFetchUserInfo($userAccessID)
/* Extract and return all user-access field values (such as username, email, active flag, etc),
while also enforcing the account to be active.
ARGUMENT: The user's access ID
RETURN VALUE: an associative array, containing the user's data. In case of error or record not found (possibly because account was inactive), return false.
*/
public function isUserActive($userAccessID)
/* Verify the validity of the user access ID. Return true iff an ACTIVE user membership with the given ID is found
*/
public function passwordRequirements()
// Return a string explaining the password requirements. The message must match the policy set in the method passwordValidateRequirements()
public function passwordValidateRequirements($pass)
// Enforce password requirements. Any change needs to be also reflected in the method passwordRequirements()templateEvaluator
Class for Template Evaluation: bare-bones counterpart of the Python library "Jinja"
DEPENDENCIES: None
USAGE EXAMPLES:
1) simple field substitution:
"Dear {{name}}, see you on {{day}}"
. This is Jinja-like (different delimeters may be requested)
2) if/then/else constructs:
{ [test] [if-branch string] [else-branch string] }
where test can be: 1) a field (true if not null) ; or 2) "field=string"
Example 1:"{ [phone] [are you still at {phone}?] [I lack your phone #] }"
Example 2:"{ [site=c] [some name] [some other name] }"
3) pre-registered, site-provided (NOT user-provided!), functions:
$templ_eval->registerFunction("foo"); // foo() become Registed Function 1 for this site
Example:"Go to link {RF(1, {productID})}"
Public Properties
public $errors = ""; // The last error message, if any
Public Methods
public function setTags($openTag, $closeTag)
// To replace the default tags, if desired
public function evaluateTemplate($template, $replaceArray, $outputErrors = true)
/* Example of template: "Dear {{name}}, see you on {{day}}!"
Example of replaceArray: array("name" => "Valerie" , "city" => "SF")
Case-sensitive.
*/
function exactStringDisplay($str, $caption = "String")
/*
This function is just for debugging.
Assemble and return the HTML to display, inside a boxed div element, the given string EXACTLY as it is,
without any of the usual HTML printing substitutions.
For example "one<br><b>two</b>" will show up exactly as composed.
*/uploader
Class to facilitate File Uploads
DEPENDENCIES:
- loggingPublic Properties
public $logger; // Object of class "logging". Use to log messages (diagnostics, etc) on the server
Public Methods
public function errorInfo()
// Return the last error message generated by any method
public function uploadFiles($upload_fileset_info, $dest_dir, $nameHandler, $postUploadHandler, $verbose = false)
/* Attempt to upload ONE OR MULTIPLE files to the specified destination directory.
Whenever each of the uploads is initially successful (got stored in a temporary file on the server), invoke the optional function stored in $nameHandler
RETURN VALUE: the number of successful uploads. In case of error, the property "errorMessage" is set to an explanation of the error
If any upload fails, the uploader skips to the next file upload
USAGE:
For a SINGLE file upload:
A) the calling form has a form with an entry such as: <INPUT type='file' name='myFile'>
B) the form-processing script contains: $upload_fileset_info = $_FILES["myFile"];
For a MULTIPLE file uploads:
A) the calling form has a set of entries such as: <INPUT type='file' name='myFileList[]'> (one per file to upload)
B) the form-processing script contains: $upload_fileset_info = $_FILES["myFileList"];
ARGUMENTS:
$upload_fileset_info (see USAGE, above)
$dest_dir a relative or absolute path in the file system, incl. the final "/" . (For the same folder, use "./") Any already-existing file with the same name will get over-written
$nameHandler If FALSE, it indicates "just use the same original name for the uploaded file"
If TRUE, it's expected to be the name of a function to invoke with 4 arguments: f($name, $tmp_name, $type, $size)
$name the original name of the file being uploaded
$tmp_name the temporary filename assigned by the server mid-upload (with full path)
$type the uploaded file's MIME type
$size size of uploaded file's size in Bytes
Tha function needs to return a file name to use for permanent file storage (possibly the same as the original name), or false in case of error (for example, a database error).
That function is also a convenient place for the invoking script to do all the necessary database operations.
$postUploadHandler name of a function that gets invoked upon a successful file upload (for example, to take care of additional database update). Use FALSE to indicate no function
$verbose whether to print out (in addition to logging) any information or error messages
$upload_fileset_info is an array of 5 elements, called:
"name",
"type",
"tmp_name",
"error",
"size"
Each element is either a value (in case of single uploads) or an array whose dimension equals the numbers of files being uploaded.
EXAMPLE of $upload_fileset_info in case of a SINGLE UPLOADED FILE:
Array
(
[name] => sample-file.doc
[type] => application/msword
[tmp_name] => /tmp/path/phpVGCDAJ
[error] => 0
[size] => 450
)
EXAMPLE of $upload_fileset_info in case of MULTIPLE FILES:
Array
(
[name] => Array
(
[0] => sample-file1.doc
[1] => focus.jpg
)
[type] => Array
(
[0] => application/msword
[1] => image/jpeg
)
[tmp_name] => Array
(
[0] => /tmp/phprS2u1w
[1] => /tmp/php3oOgSr
)
[error] => Array
(
[0] => 0
[1] => 0
)
[size] => Array
(
[0] => 22685
[1] => 30733
)
)
*/
public function uploadSingleFile($name, $type, $size, $tmp_name, $error, $dest_dir, $nameHandler, $postUploadHandler, $verbose = false)
/* The first 5 arguments are the standard $_FILES array component. For an explanation of the remaining arguments, see uploadFiles()
If a file with the requested name already exists, it gets overwritten.
Return 1 if upload is successful, or 0 if not (i.e. the number of successful uploads). In case of error, the property "errorMessage" is set to an explanation of the error
*/