RM - Source Code View

Source Code Viewer

This page lets you view source code from my server. The program uses a brute force code formatter to color code elements. NOTE: I wrote this program while trying to learn the vim text editor. This is not my usual coding style.

Use this select box to select a file.

Hide Lines Numbers

View: Resource Model

The Resource Model is the base code that I use in a variety of sites.

Formatted Code

Below is the code all formatted with bright colors. The program links to files opened with include() and expands those opened with require(). Clicking on the require line should change visibility.

Resource Model

001 <?php 002 // 12/26/2015: NOTE I am rewriting this from scratch. 003 // I will write a blog post explaining why when I am finished. 004 // This is closer to the code I actually use and like to use. 005 // You see test variables at the e bottom. Those will go away soon 006 // I am using thise to help debug. 007 // 008 // I will remove theses after writing the last function, then 009 // run the program through performance and QA tests. 010 011 012 /** 013 * The Resource Model for Web Design holds that certain objects in a 014 * program such as the database connection, notification object, output 015 * buffer and site definition are resources that should be global and 016 * This page includes the resources. 017 * 018 * @package ResourceModel 019 * @copyright 2002-2016 Kevin Delaney 020 * @license https://yintercept.com/resources/license.html 021 * @see https://yintercept.com/resources 022 * @see https://yintercept.com/resources/view.php?script=1 Source Code 023 * @version 0.0.10 024 * 025 * 026 */ 027 028 const TAB = ' '; 029 030 // This file has the notification object
require('/var/www/php/msg.php');
001 <?php 002 /** 003 * @package ResourceModel 004 * 005 * msgNote() functions create a poor man's notification object. 006 * 007 * msgNote() conveys general information and success messages to the user 008 * msgComment() leaves comments on the view source page for visitors and 009 * as [DEBUG] warnings for the programmer and tester. 010 * msgError() produces messages that show up in red. It sets isOkay to false 011 * You can view this code on the msg code Test page: 012 * @see https://yintercept.com/resources/view.php?script=9 013 * 014 * The function msgHTML() prints the messages and resets the buffer. 015 * @author Kevin Delaney 016 * @copyright 1999-2015 Kevin Delaney 017 * @license https://yintercept.com/resources/license.html 018 */ 019 const MSG_OK = 1; // denotes a happy message 020 const MSG_ERROR = 2; // denotes a sad message 021 const MSG_COMMENT = 3; // print as a comment or DEBUG notice. 022 // NOTE: As this "object" is a global, I will put its data in $GLOBALS 023 $GLOBALS['msg']['arr'] = array(); // holds the messages 024 $GLOBALS['msg']['isOkay'] = true; // warns that an error occurred 025 $GLOBALS['msg']['debugMode'] = false; // if true msgHTML() will display comments for programmer 026 /** 027 * msgNote() adds a neutral or success message to the buffer. 028 * @param string $txt The text of a godd message 029 */ 030 function msgNote($txt='') { 031 if($txt!='') $GLOBALS['msg']['arr'][] = ['type'=>MSG_OK, 'txt' => $txt]; 032 } 033 /** 034 * @param string $txt The text to give the user on error. 035 */ 036 function msgError($txt='') { 037 if ($txt != '') $GLOBALS['msg']['arr'][] = ['type'=>MSG_ERROR, 'txt' => $txt]; 038 $GLOBALS['msg']['isOkay'] = false; 039 } 040 /** 041 * @param string $txt Text of a comment for the programmer. 042 */ 043 function msgComment($txt='') { 044 if ($txt!='') $GLOBALS['msg']['arr'][] = ['type'=>MSG_COMMENT, 'txt' => $txt]; 045 } 046 /** 047 * msgToggle saves a success or failure message based on the toggle 048 * @PARAM boolean $test 049 * @PARAM array $msgArr This three part array holds the message. 050 * $msgArr[0] is a required success message. 051 * $msgArr[1] is a reuired failure message 052 * $msgArr[2] is an optional failure comment (with debug notes) 053 * $msgArr[3] is optional message if test is not boolean. 1=return error. * @RETURN function resturns a boolean. 054 */ 055 function msgToggle($test, $msgArr) { 056 $rv = $test; 057 if (is_integer($test)) $test = (bool) $test; 058 // some functions say they return boolean, then don't. 059 if (!(is_bool($test))) { 060 msgError($msgArr[1]); 061 msgComment('Test needs to be boolean'); 062 $rv = false; 063 } elseif ($test) { 064 // success message. 065 if (isset($msgArr[0])) msgNote($msgArr[0]); 066 } else { 067 if (isset($msgArr[1])) msgError($msgArr[1]); 068 if (isset($msgArr[2])) msgComment($msgArr[2]); 069 } 070 return $rv; 071 } 072 /** 073 * I used to call this function isOkay(); I changed name to msgOkay() 074 * to emphasize that this really is an object even though it's not in a class. 075 * @return boolean Returns true if no errors and false on error 076 */ 077 function msgOkay() { 078 return $GLOBALS['msg']['isOkay']; 079 } 080 /** 081 * @output function prints the array in HTML 082 */ 083 function msgHTML($indent=PHP_EOL.' ') { 084 // This control loops through and prints the messages. 085 static $blockId = 0; // give each message block an id 086 $cnt = count($GLOBALS['msg']['arr']); // count messages 087 088 if ($cnt > 0) { 089 $ulClass = ($GLOBALS['msg']['isOkay'])? 'ulOkay' : 'ulErrors'; 090 $blockStr = ($blockId++ == 0)? 'msg' : 'msg'.$blockId; 091 echo $indent.'<ul id="'.$blockStr.'" class="'.$ulClass.'">'; 092 093 for($i=0; $i<$cnt; $i++) { 094 $row=$GLOBALS['msg']['arr'][$i]; 095 switch ($row['type']) { 096 case MSG_OK: 097 echo PHP_EOL.' <li class="msgHappy">'.$row['txt'].'</li>'; 098 break; 099 case MSG_ERROR: 100 echo PHP_EOL.' <li class="msgSad">'.$row['txt'].'</li>'; 101 break; 102 case MSG_COMMENT: 103 if ($GLOBALS['msg']['debugMode']) { 104 // this is a message for the administrator. 105 echo PHP_EOL.' <li>[DEBUG]'.$row['txt'].'[/DEBUG]</li>'; 106 } else { 107 echo PHP_EOL.'<!--'.$row['txt'].'-->'; 108 } 109 break; 110 default: // should not happen. 111 } 112 } 113 echo PHP_EOL.'</ul>'; 114 $GLOBALS['msg']['arr'] = []; // empty the array. 115 } 116 } 117 /** 118 * msgLog() records information into the log database 119 * @param string logName is the name of a table in the log. 120 * @param arr is an array that should match the columns in the log. 121 */ 122 function msgLog($logName,$arr=[]) { 123 file_put_contents('/var/www/db/'.$logName.'.log',implode('|',$arr), FILE_APPEND | LOCK_EX); 124 } 125 ?>
// End Require
031 032 033 // This page includes the SQL functions
require('/var/www/php/sql.php');
001 <?php 002 /** 003 * The sql functions connect with the database using PDO and PDOStatement. 004 * NOTE, you will need to manually edit dbConn to configure the connections. 005 * 006 * @package ResourceModel 007 * @copyright 2002-2016 Kevin Delaney 008 * @license https://yintercept.com/resources/license.html 009 * @see https://yintercept.com/resources 010 * @see https://yintercept.com/resources/view.php?script=1 Source Code 011 * @version 0.0.10 012 * 013 * These are the primary functions: 014 * sqlValue() returns a single value from the database 015 * sqlRow() returns a single row from the database. 016 * sqlArr() returns a 2D array with full result set. 017 * sqlLoop @see https://yintercept.com/resources/view.php?script=10 018 * dbConn() holds the PDO Connect object. It has some additional functions: 019 * Call it with DB_BEGIN, DB_ROLLBACK & DB_COMMIT for transactions 020 * "+dbname" will run an 'ATTACH DATABASE' command 021 * DB_INSERT_ID will return the last insert id 022 * The function returns a PDOStatement if you want one of those. 023 * 024 */ 025 const SDB_PATH = '/var/www/db/'; // Location of my SQLite3 databases. 026 const DB_MAIN = 'main'; // name of your primary database 027 // dbConn will process these transactions 028 const DB_INSERT_ID =1; // returns last insert id 029 const DB_BEGIN = 3; // Begin a transaction 030 const DB_ROLLBACK = 4; // rollback a database transaction 031 const DB_COMMIT = 5; // commit a database transaction 032 const DB_CLOSE = 6; // closes connection, logs stats & returns dbCnt. 033 const DB_CNT = 7; // dbConn returns statement count. 034 const DB_ERRORS = 666; // dbConn returns error info. 035 // used by sqlRow() 036 const DB_CHK = 100; 037 const DB_MAX_ROWS = 2000; 038 // directives for sqlExec 039 const DB_ONE_MAX = 64; // rollback if more than one row updated 040 const DB_ROWCOUNT = 10; // return the row count 041 const DB_LAST_ID = 1; // same as DB_INSERT_ID 042 // include('/var/www/php/cnx.php'); 043 /** 044 * dbConn is a database connection factory that maintains an array of 045 * connections and returns PDOStatements to sql requests. 046 * Currently, I am hard coding the connectson in deConn. 047 * in a future release, connection information will be in an array 048 * 049 * @param string $sql is either SQL command or a short cut code. 050 * @param string $dbi identifies the database to use. 051 * @return mixed[] returns a PDOStatement for SQL command or info related to call. 052 */ 053 function dbConn($dbi,$sql) { 054 static $dbh = array(); // array holds the PDO objects. 055 static $dbCnt = 0; // counts calls to the database 056 static $dbTrace = ''; // holds a trace string. 057 /** register your conections here. The values of the array are: 058 * @param string dbnam -- give each db a unique name. Use 'main' for default. 059 * @param integer status starts as 0. Is 1 if connected and -1 if failed. 060 * @param string dsn is the Data Name Source for the connection 061 * @param string user is the database user name 062 * @param string pwd is the password. 063 */ 064 static $connArr = array( 065 DB_MAIN=>['status'=>0,'dsn'=>'sqlite:/var/www/db/main.db','user'=>'','pwd'=>''], 066 'log'=>['status'=>0,'dsn'=>'sqlite:/var/www/db/log.db','user'=>'','pwd'=>''] 067 ); 068 // I've defined four databases. ele is hosted by http://www.elephantsql.com 069 // the last database is a mistake ... used to test db failures. 070 $rv = false; 071 $dbCnt++; 072 $stmt = false; 073 try { 074 // verify dbi exists in the index. 075 if (isset($connArr[$dbi])) { 076 if ($connArr[$dbi]['status'] == -1) throw new Exception('No Database Connection.'); 077 } else { 078 throw new Exception('Connection "'.$dbi.'" does not exist.'); 079 } 080 if ($connArr[$dbi]['status']==0) { 081 // connect to the database 082 try { 083 $dbh[$dbi] = new PDO($connArr[$dbi]['dsn'],$connArr[$dbi]['user'],$connArr[$dbi]['pwd']); 084 $connArr[$dbi]['status'] = 1; 085 } catch(PDOException $e) { 086 // record error and set DB_ACTIVE to DB_NONE. 087 $connArr[$dbi]['status'] = -1; 088 // log database connection faxlure in a file 089 file_put_contents(SDB_PATH.'pdoerr.txt', $dbi.' //'.$_SERVER['REQUEST_TIME'].' '.$e->getMessage(), FILE_APPEND | LOCK_EX); 090 throw new Exception('Database connection '.$dbi.' failed.'); 091 } 092 } 093 // We can add little short cuts here. such as +str Attaches a database 094 if (substr($sql,0,1)=='+') { 095 if (substr_count($sql,' ')==0) { 096 $asql = 'ATTACH DATABASE '.$dbh[$dbi]->quote(SDB_PATH.substr($sql,1).'.db').' AS '.$dbh[$dbi]->quote(substr($sql,1)); 097 $dbh[$dbi]->exec($asql); 098 $dbTrace+='A'; 099 $rv = true; 100 } 101 } elseif (substr($sql,0,1) == '^') { 102 $rv = $dbh[$dbi]->quote(substr($sql,1)); 103 } elseif ($sql==DB_INSERT_ID) { 104 $rv = $dbh[$dbi]->lastInsertId(); 105 } elseif ($sql==DB_ERRORS) { 106 $rv = implode('|',$dbh[$dbi]->errorInfo()); 107 } elseif ($sql == DB_CNT) { 108 $rv = --$dbCnt; // return current dbCnt (minus this call) 109 } elseif ($sql==DB_CLOSE) { 110 $dbh[$dbi]=null; 111 $dbErrors=false; 112 $rv=$dbCnt; 113 // store a trace of page for later analysis. 114 // $page_id = 0; // will populate later. 115 msgLog('dbTrace',[0,$dbTrace]); 116 } elseif ($sql==DB_BEGIN) { 117 if ($dbh[$dbi]->inTransaction()) { 118 msgComment($dbi.' is already in transaction mode.'); 119 } else { 120 $dbh[$dbi]->beginTransaction(); 121 } 122 $rv = true; 123 } elseif ($sql==DB_ROLLBACK) { 124 if ($dbh[$dbi]->inTransaction()) { 125 $dbh[$dbi]->rollBack(); 126 } else { 127 msgComment('Rollback outside a transaction'); 128 } 129 $rv = true; 130 } elseif ($sql==DB_COMMIT) { 131 if ($dbh[$dbi]->inTransaction()) { 132 $dbh[$dbi]->commit(); 133 } else { 134 msgComment('Attempting to commit outside transaction.'); 135 } 136 $rv = true; 137 } else { 138 // return an unexecuted prepared statement to call procedure. 139 $dbTrace.=substr($sql,0,1); // add first letter of command to trace. 140 $rv = $dbh[$dbi]->prepare($sql); 141 } 142 } catch (Exception $e) { 143 msgError('PDO says: '.$e->getMessage()); 144 $rv = false; 145 } 146 return $rv; 147 } 148 /** 149 * sqlValue returns a single value from the database. 150 * @param string $sql is a SQL SELECT command 151 * @param array $arr holds parameters for the SQL command 152 * @return string The function returns the first column of first row of the result 153 */ 154 function sqlValue($sql,$arr=[],$dbi=DB_MAIN) { 155 $stmt = dbConn($dbi,$sql); 156 // return an array of zeros on failure. 157 $rv = ''; // returns a blank space on error 158 if (is_object($stmt)) { 159 if (!is_array($arr)) { 160 $tst = $arr; 161 // no need to get huffy is some forgot the brackets to make it an array. 162 if (is_string($tst) or is_numeric($tst)) { 163 $arr=[$tst]; 164 // msgComment('sqlValue - string to array'); 165 } else { 166 msgError('In valid parameter for sqlValue()'); 167 } 168 } 169 if ($stmt->execute($arr)) { 170 $row = $stmt->fetch(PDO::FETCH_NUM); 171 $rv = (isset($row[0]))? $row[0] :''; 172 } else { 173 msgError('sqlValue execute failed'); 174 msgComment($sql.'<br />Parameters = '.implode('|',$arr)); 175 } 176 } else { 177 // this should only happen with a bad SQL statement 178 msgError('sqlValue() call failed. call #'.dbConn(DB_MAIN,DB_CNT)); 179 msgComment($dbi.' '.$sql); 180 // msgComment('RV Datatype is '.gettype($stmt)); 181 } 182 return $rv; 183 } 184 /** sqlRow() retruns a row for a SQL command. The command buffers PDOStatement 185 * You can get multiple rows and use function in some loops 186 * Use sqlLoop for complex loops. 187 * @param string $sql is a SQL select command. If null; returns next row of last 188 * command. If $sql==DB_CHK it checks to see if their is a next row. 189 * @param array $arr holds variables for the SQL statement 190 * @param integer $fetchStyle determines the PDO fetch style 191 * @return mixed[] Function returns an array for sql calls or boolean for chk. 192 */ 193 function sqlRow($sql=null,$arr=null,$dbi=DB_MAIN,$fetchStyle=PDO::FETCH_NUM) { 194 static $stmt=null; 195 static $style = ''; 196 static $chkCnt = 0; 197 static $chkVal = false; 198 $rv = [false,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; 199 if ($sql == DB_CHK) { 200 $rv = ($chkCnt++ > DB_MAX_ROWS )? false : $chkVal; 201 } elseif ($sql == null) { 202 // get the next row from the existing counter. 203 $rv = $stmt->fetch($fetchStyle); 204 if ($rv===false) { 205 $rv = ($fetchStyle==PDO::FETCH_OBJ)? null : [false,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; 206 $chkVal = false; 207 } else { 208 $chkVal = true; 209 } 210 } else { 211 // get and execute a PDOStatement 212 $stmt = dbConn($dbi,$sql); 213 $style = $fetchStyle; 214 $chkVal=false; 215 if (gettype($stmt) == 'object') { 216 if ($stmt->execute($arr)) { 217 $rv = $stmt->fetch($fetchStyle); 218 if ($rv===false) { 219 $rv = ($fetchStyle==PDO::FETCH_OBJ)? null : [false,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; 220 } else { 221 $chkVal = true; 222 } 223 } else { 224 $stmt = null; 225 msgError('SQL execute failed'); 226 msgComment($sql); 227 msgComment('Parameters = '.implode('|',$arr)); 228 } 229 } else { 230 // this should only happen with a bad SQL statement 231 msgError('SQL call failed.'); 232 msgComment($sql); 233 msgComment('RV Datatype is '.gettype($stmt)); 234 } 235 } 236 return $rv; 237 } 238 /** 239 * sqlAll() returns the entire result set as a two dimensional array. 240 * @param string $sql is a SQL SELECT Statement 241 * @param array The $arr array holds values for for the SQL statement 242 * @param integer $fetchStyle is a PDO::FETCH style option 243 * @param array function always returns an array. 244 */ 245 function sqlAll($sql,$arr,$dbi=DB_MAIN,$fetchStyle=PDO::FETCH_NUM) { 246 $stmt = dbConn($dbi,$sql); 247 // return an array of zeros on failure. 248 $rv = array(); // returns a blank space on error 249 if (is_object($stmt)) { 250 if ($stmt->execute($arr)) { 251 // I break out of the function to avoid making an extra copy of results 252 return $stmt->fetchAll($fetchStyle); 253 } else { 254 msgError('SQL execute failed'); 255 msgComment($sql.'<br />Parameters: '.implode('|',$arr)); 256 } 257 } else { 258 // this should only happen with a bad SQL statement 259 msgError('SQL call failed.'); 260 msgComment($sql.'<br />RV Datatype is '.gettype($stmt)); 261 } 262 // successful calls return results straignt from driver. 263 return array(); // returns empty array on failure 264 } 265 /** 266 * sqlExec() execute a DML SQL command such as INSERT OR UPDATE 267 * @param string $sql is the SQL command to upddate 268 * @param array $arr contains variables for the SQL 269 * @param integer $directive determines the output of the command The default 270 is to return a row. DB_LAST_ID returns the last insert id. 271 DB_ONE_MAX rollsback transacation if it affects more than one row. 272 * @param string $successMsg is printed on cuccesful execution. 273 Program replaces %ID with insert id and %RS or %RC with row count. 274 * @param string $failureMsg is printed on failure of the statement 275 * @output db Updates the database 276 * @return either the row count or insert id based on $directive 277 */ 278 function sqlExec($sql,$arr,$dbi=DB_MAIN,$successMsg='',$failureMsg='',$directive=0) { 279 if ($directive==0) $directive = (substr($sql,0,6) == 'INSERT')? DB_INSERT_ID : DB_ROWCOUNT; 280 if ($directive == DB_ONE_MAX) dbConn($dbi,DB_BEGIN); 281 $stmt = (msgOkay())? dbConn($dbi,$sql) : 123; 282 $rv = 0; // returns rows affected. 283 if (is_object($stmt)) { 284 if ($stmt->execute($arr)) { 285 // prepare message. 286 $rv = $stmt->rowCount(); // rowcount is the default return value. 287 $rowStr = $rv.' rows'; 288 if ($directive == DB_ONE_MAX) { 289 if ($rv > 1) { 290 msgError('SQL Warning. '.$rowStr.' affected on single row query.<br />Rolling back transaction.'); 291 dbConn($dbi,DB_ROLLBACK); 292 msgComment('Rolled back: '.$sql); 293 } else { 294 dbConn($dbi,DB_COMMIT); 295 } 296 } 297 if ($rv == 0) { 298 $rowStr = 'no rows'; 299 } elseif ($rv == 1) { 300 $rowStr = '1 row'; 301 } 302 $insertId = dbConn($dbi,DB_INSERT_ID); 303 if ($successMsg != '') msgNote(str_replace(['%ID','%RS','%RC'],[$insertId,$rowStr,$rv],$successMsg)); 304 if ($directive == DB_INSERT_ID) $rv = $insertId; 305 } else { 306 msgError('SQL Exec: '.$failureMsg); 307 msgComment($sql.'<br />Parameters = '.implode('|',$arr).'<br />Error '.implode('|',$stmt->errorInfo())); 308 } 309 } elseif ($stmt== 123) { 310 // msgOkay reported an error. 311 } else { 312 // this should only happen with a bad SQL statement 313 msgError('SQL Call: '.$failureMsg); 314 msgComment($sql.'<br />'.implode(',',$arr).'<br />Return Datatype is '.gettype($stmt)); 315 msgComment('DB Error '.dbConn($dbi,DB_ERRORS)); 316 } 317 return $rv; 318 } 319 ?>
// End Require
034 035 036 /** 037 * Rather than creating a WebSite Object, I just load the site data into globals 038 * @GLOBAL array rmSite contains the Web Site name, ID and format information 039 */ 040 $GLOBALS['rmSite'] = sqlRow('SELECT site_id as \'id\', collection_id as \'defColl\', site_nm as \'name\', domain_nm as \'domain\', julianday(\'now\') as \'now\', date() as \'date\', 1 as css_id FROM Web_Site WHERE domain_nm LIKE ?',[strtolower($_SERVER['HTTP_HOST'])],DB_MAIN,PDO::FETCH_OBJ); 041 042 043 // ipInfo and Prcedures that logs hits 044 include('/var/www/php/log.php'); 045 // I create an ipInfo object from log in global context so we can refer to it. 046 $rmIP = new ipInfo(); 047 $rmIP->loadIP(); 048 049 050 /** 051 * Rather than creating a WebSite Object, I just load the site data into globals 052 * @GLOBAL array rmSite contains the Web Site name, ID and format information 053 */ 054 $GLOBALS['rmSite'] = sqlRow('SELECT site_id as \'id\', collection_id as \'defColl\', site_nm as \'name\', domain_nm as \'domain\' FROM Web_Site WHERE domain_nm LIKE ?',[strtolower($_SERVER['HTTP_HOST'])],DB_MAIN,PDO::FETCH_OBJ); 055 056 // I use the variable $btn for flow control in most pages; so I will grab the 057 // contents from $_REQUEST (which combines $_POST and $_GET. 058 059 $btn = (isset($_REQUEST['btn']))? $_REQUEST['btn'] : ''; 060 061 ?>

Use "view source" from your browser to grab the output. Feel free to link to this project and check out the Resource Model for information on PHP coding or my tumblr blog for picture of Arizona, Colorado or Utah.

File last modified at April 28 2020 21:01:10.. This page has been viewed 3959 Times.

Record of Revisions
RevbyDateDescription
0.7kd2015-12-31Added block id to message block and converted msgHTML() from foreach back to while.
0.6kd2015-12-30Moved the SQL commands and msg into their own files. Added the sqlExec() command which modifies the database
0.5kd2015-12-31Changed comment style to match that of docBlock standard. Added reporting to dbConn
0.4kd2015-12-27Created the sqlAll() function which returns the full result set a two dimensional array. NOTE: In legacy code, I call the function sqlArray().
0.3kd2015-12-27Deleted sqlLoop() function. Changed sqlRow() so that it works in loops.
0.2kd2015-12-26Rewrote the entire program as a collection of flat functions. Created sqlLoop() function.

blog ~ Resource Model ~ links