| 1 | rodolico | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           | 13 | rodolico | 3 |    /*
 | 
        
           |  |  | 4 |     * Class DBQuery
 | 
        
           |  |  | 5 |     * 
 | 
        
           | 16 | rodolico | 6 |     * PHP Class as a wrapper around the mysqli class. Allows you to make
 | 
        
           | 13 | rodolico | 7 |     * the connection and run queries and/or DDL's if needed with the
 | 
        
           | 16 | rodolico | 8 |     * most common parameters (ie, not as flexible as mysqli itself).
 | 
        
           | 13 | rodolico | 9 |     * Enhancements include:
 | 
        
           |  |  | 10 |     *
 | 
        
           |  |  | 11 |     * Logs all queries with an optional username except select statements
 | 
        
           |  |  | 12 |     * ie, anything that will change the system ( insert, delete, update,
 | 
        
           |  |  | 13 |     * create, drop, alter, etc... ). Statement are logged with a date/time
 | 
        
           |  |  | 14 |     * stamp, the user who performed the function, and the query executed
 | 
        
           |  |  | 15 |     * 
 | 
        
           |  |  | 16 |     * Errors are simply trapped and the message is stored in the public
 | 
        
           |  |  | 17 |     * member $error. Functions which find an error return boolean false
 | 
        
           |  |  | 18 |     *
 | 
        
           |  |  | 19 |     * Public Functions are:
 | 
        
           |  |  | 20 |     *    __construct   -- creates connection
 | 
        
           |  |  | 21 |     *    doSQL         -- executes an SQL query or DDL
 | 
        
           |  |  | 22 |     *    errors        -- returns all errors not yet cleared
 | 
        
           |  |  | 23 |     *                     can also directly access via public member errors
 | 
        
           |  |  | 24 |     *
 | 
        
           |  |  | 25 |     *    public static function makeSafeSQLConstant ( $value, $type='S', $default='null', $falsetrue='10' ) {
 | 
        
           | 1 | rodolico | 26 |   | 
        
           | 4 | rodolico | 27 |   | 
        
           | 13 | rodolico | 28 |     * Author: R. W. Rodolico (rodo@dailydata.net)
 | 
        
           |  |  | 29 |     * Date: 2018-04-30
 | 
        
           |  |  | 30 |     * 
 | 
        
           |  |  | 31 |     */
 | 
        
           |  |  | 32 |   | 
        
           |  |  | 33 |    class DBQuery extends mysqli {
 | 
        
           |  |  | 34 |   | 
        
           |  |  | 35 |       /* everything is in the one $parameters array, which can then
 | 
        
           |  |  | 36 |        * be set/saved/loaded as needed
 | 
        
           |  |  | 37 |        */
 | 
        
           | 6 | rodolico | 38 |       protected $parameters = array(
 | 
        
           | 13 | rodolico | 39 |              // query(s) to be run
 | 
        
           |  |  | 40 |              'query' => '',
 | 
        
           |  |  | 41 |              // additional clause to be added to where on a statement
 | 
        
           |  |  | 42 |              'whereClause' => '',
 | 
        
           |  |  | 43 |              // additional order by clause to be added
 | 
        
           |  |  | 44 |              'orderBy' => '',
 | 
        
           |  |  | 45 |              // the number of rows 1) affectd by an insert, update or delete or 2) returned by a select
 | 
        
           |  |  | 46 |              'rowsAffected' => '',
 | 
        
           |  |  | 47 |              // the value of the last row inserted
 | 
        
           |  |  | 48 |              'lastInsertKey' => '',
 | 
        
           |  |  | 49 |              // an array that contains the data retrieved by a query (select only)
 | 
        
           | 29 | rodolico | 50 |              'returnData' => array(),
 | 
        
           | 13 | rodolico | 51 |              // an array that contains the meta data from the query for each column
 | 
        
           | 29 | rodolico | 52 |              'columnMetaData' => array(),
 | 
        
           | 13 | rodolico | 53 |              // number of columns (fields) returned by query (select only)
 | 
        
           |  |  | 54 |              'numfields' => 0,
 | 
        
           |  |  | 55 |              // type of data returned, array of array, array of hash, or both
 | 
        
           | 36 | rodolico | 56 |              'fetchType' => MYSQLI_BOTH,
 | 
        
           | 13 | rodolico | 57 |              // human readable form of fetchType
 | 
        
           | 36 | rodolico | 58 |              'returnType' => 'both',
 | 
        
           | 13 | rodolico | 59 |              // array of any errors which occurred
 | 
        
           |  |  | 60 |              'error' => array(),
 | 
        
           |  |  | 61 |              // if set to a table name, all modifying queries are written to it
 | 
        
           |  |  | 62 |              // must contain, at a minimum, columns whenrun timestamp, whoran varchar and query text
 | 
        
           |  |  | 63 |              // can be created with $this->buildAuditTable
 | 
        
           |  |  | 64 |              'auditTable' => '_activity_log'
 | 
        
           |  |  | 65 |            );
 | 
        
           |  |  | 66 |   | 
        
           | 37 | rodolico | 67 |       public function __construct( $server, $username, $password, $database ) {
 | 
        
           |  |  | 68 |          parent::__construct(  $server, $username, $password, $database );
 | 
        
           |  |  | 69 |       }
 | 
        
           | 4 | rodolico | 70 |   | 
        
           | 13 | rodolico | 71 |       /*
 | 
        
           |  |  | 72 |        * static function which simply parses out an error and returns
 | 
        
           |  |  | 73 |        * a string suitable for printing. NOTE: it includes line returns
 | 
        
           |  |  | 74 |       */
 | 
        
           |  |  | 75 |       public static function error2String( $error ) {
 | 
        
           |  |  | 76 |          $return = array();
 | 
        
           |  |  | 77 |          for ( $i = 0; $i < count( $error ); $i++ ) {
 | 
        
           |  |  | 78 |             $return[] = implode( "\n", array( 'Error ' . $error[$i]['id'] . ' - ' . $error[$i]['message'], 'while executing query', $error[$i]['query'] ) );
 | 
        
           | 6 | rodolico | 79 |          }
 | 
        
           | 13 | rodolico | 80 |          return implode( "\n\n", $return ) . "\n";
 | 
        
           |  |  | 81 |       } // error2String
 | 
        
           | 6 | rodolico | 82 |   | 
        
           | 13 | rodolico | 83 |       // simply returns parameters for saving and reloading later
 | 
        
           | 6 | rodolico | 84 |       public function save() {
 | 
        
           |  |  | 85 |          return $this->parameters;
 | 
        
           |  |  | 86 |       } // function save
 | 
        
           | 13 | rodolico | 87 |   | 
        
           |  |  | 88 |       // loads parameters from $this->save(), or hand built
 | 
        
           | 6 | rodolico | 89 |       public function load( $parameters = array() ) {
 | 
        
           |  |  | 90 |          $this->parameters = $parameters;
 | 
        
           |  |  | 91 |       } // function load
 | 
        
           |  |  | 92 |   | 
        
           | 13 | rodolico | 93 |       // sets a key/value pair in $this->parameters
 | 
        
           |  |  | 94 |       // returns $value
 | 
        
           | 1 | rodolico | 95 |       public function __set( $name, $value ) {
 | 
        
           | 6 | rodolico | 96 |          $this->parameters[$name] = $value;
 | 
        
           | 4 | rodolico | 97 |          return $value;
 | 
        
           | 1 | rodolico | 98 |       }
 | 
        
           | 13 | rodolico | 99 |   | 
        
           |  |  | 100 |       // gets the current value of $this->parameters[$name]
 | 
        
           | 1 | rodolico | 101 |       public function __get( $name ) {
 | 
        
           | 6 | rodolico | 102 |          return isset( $this->parameters[$name] ) ? $this->parameters[$name] : null;
 | 
        
           | 1 | rodolico | 103 |       }
 | 
        
           | 13 | rodolico | 104 |   | 
        
           |  |  | 105 |       // returns true if $parameters[$name] is set
 | 
        
           | 1 | rodolico | 106 |       public function __isset( $name ) {
 | 
        
           | 6 | rodolico | 107 |          return isset( $this->parameters[$name] );
 | 
        
           | 1 | rodolico | 108 |       }
 | 
        
           | 13 | rodolico | 109 |   | 
        
           |  |  | 110 |   | 
        
           |  |  | 111 |       /*
 | 
        
           |  |  | 112 |        * function: buildAuditTable
 | 
        
           |  |  | 113 |        * parameters:
 | 
        
           |  |  | 114 |        *       $tablename - name of table to be built
 | 
        
           |  |  | 115 |        *       $createStatement - SQL DDL to build the table
 | 
        
           |  |  | 116 |        *
 | 
        
           |  |  | 117 |        * If $tablename is set, will use that, otherwise will use
 | 
        
           |  |  | 118 |        * $parameters[auditTable]. In either case, $parameters[auditTable]
 | 
        
           |  |  | 119 |        * is set to the value of the table used
 | 
        
           |  |  | 120 |        * 
 | 
        
           |  |  | 121 |        * if $createStatement is set, will be run WITHOUT MODIFICATION, and
 | 
        
           |  |  | 122 |        * $parameters[auditTable] is not set to anything (unless $tablename
 | 
        
           |  |  | 123 |        * is set)
 | 
        
           |  |  | 124 |        *
 | 
        
           |  |  | 125 |        * If $createStatement is not set, will use a default to build
 | 
        
           |  |  | 126 |        * a table from $parameters[auditTable].
 | 
        
           |  |  | 127 |        *
 | 
        
           |  |  | 128 |        * Can definitely blow up if the table name is not set both places
 | 
        
           |  |  | 129 |        * or if $createStatement is fubar
 | 
        
           |  |  | 130 |        */
 | 
        
           |  |  | 131 |   | 
        
           |  |  | 132 |       public function buildAuditTable( $tablename = '', $createStatement = '' ) {
 | 
        
           |  |  | 133 |          if ( $tablename ) // they sent us one, so set it
 | 
        
           |  |  | 134 |             $this->parameters[ 'auditTable' ] = $tablename;
 | 
        
           |  |  | 135 |          if ( ! $createStatement ) { // they did not set createStatement, so use our default
 | 
        
           | 15 | rodolico | 136 |             $auditTable = $this->parameters['auditTable'];
 | 
        
           | 13 | rodolico | 137 |             $createStatement = "
 | 
        
           | 29 | rodolico | 138 |                create table if not exists $auditTable (
 | 
        
           | 13 | rodolico | 139 |                   _activity_log_id int unsigned not null auto_increment,
 | 
        
           |  |  | 140 |                   timestamp  timestamp,
 | 
        
           |  |  | 141 |                   user       varchar(64),
 | 
        
           |  |  | 142 |                   query      text,
 | 
        
           |  |  | 143 |                   primary key(_activity_log_id)
 | 
        
           |  |  | 144 |                ) comment 'tracks queries which modify data'";
 | 
        
           |  |  | 145 |          } // if
 | 
        
           |  |  | 146 |          if ( parent::query( $createStatement ) === false ) {
 | 
        
           |  |  | 147 |             // on error, die
 | 
        
           |  |  | 148 |             print "Can not create audit table with query<br />\n$createStatement<br />\n";
 | 
        
           |  |  | 149 |             die ( printf("Errormessage: %d - %s\n", $this->errno, $this->error ) );
 | 
        
           |  |  | 150 |          } // if error, die
 | 
        
           |  |  | 151 |       } // buildAuditTable
 | 
        
           |  |  | 152 |   | 
        
           |  |  | 153 |       /*
 | 
        
           |  |  | 154 |        * log queries to a table, file, or nothing
 | 
        
           |  |  | 155 |        * log contains date/time, username and query
 | 
        
           |  |  | 156 |        * to turn off logginging:
 | 
        
           |  |  | 157 |        * unset( $parameters['auditTable'] )
 | 
        
           |  |  | 158 |        */
 | 
        
           |  |  | 159 |       private function logIt( $username, $query, $recursion = false ) {
 | 
        
           |  |  | 160 |          if ( ! isset( $this->parameters['auditTable'] ) )
 | 
        
           |  |  | 161 |             return; 
 | 
        
           |  |  | 162 |          $username = $this->real_escape_string( $username );
 | 
        
           |  |  | 163 |          $query = $this->real_escape_string( $query );
 | 
        
           | 15 | rodolico | 164 |          $logEntry = "insert into " . $this->parameters['auditTable'] . " (user, query) values ( '$username', '$query')";
 | 
        
           | 13 | rodolico | 165 |          //print "Loggging\n$logEntry\n";
 | 
        
           |  |  | 166 |          if ( parent::query( $logEntry ) !== false ) { // good
 | 
        
           |  |  | 167 |             return;
 | 
        
           |  |  | 168 |          } else { // we had an error
 | 
        
           |  |  | 169 |             if ( ! $recursion && $this->errno == 1146 ) { // table doesn't exist, so let's create it
 | 
        
           | 15 | rodolico | 170 |                $result = parent::query( "show tables like '" . $this->parameters['auditTable'] . "'" );
 | 
        
           | 13 | rodolico | 171 |                if ( $result->num_rows == 0 ) {
 | 
        
           |  |  | 172 |                   $this->buildAuditTable( );
 | 
        
           |  |  | 173 |                   return $this->logIt( $username, $query, true );
 | 
        
           |  |  | 174 |                }
 | 
        
           | 6 | rodolico | 175 |             } else {
 | 
        
           | 13 | rodolico | 176 |                print "Trying to log transaction with query<br />\n$logEntry<br />\n";
 | 
        
           |  |  | 177 |                die ( printf("Errormessage: %d - %s\n", $this->errno, $this->error ) );
 | 
        
           |  |  | 178 |             } // if..else
 | 
        
           | 1 | rodolico | 179 |          } // if
 | 
        
           | 13 | rodolico | 180 |       } // function logIt
 | 
        
           |  |  | 181 |   | 
        
           |  |  | 182 |   | 
        
           |  |  | 183 |       /*
 | 
        
           |  |  | 184 |        * doSQL
 | 
        
           |  |  | 185 |        * Parameters: $query - string or array of strings to be executed
 | 
        
           |  |  | 186 |        *             $parameters - hash used to pass additional parameters, to include
 | 
        
           |  |  | 187 |        *                           $parameters['username'] = 'fred'; // username for logging
 | 
        
           | 36 | rodolico | 188 |        *                           $parameters['returnType'] = 'hash'; or array or both
 | 
        
           | 13 | rodolico | 189 |        * 
 | 
        
           |  |  | 190 |        * executes one or more queries
 | 
        
           |  |  | 191 |        *
 | 
        
           |  |  | 192 |        * If the query is one of select, show, describe or explain, it must
 | 
        
           |  |  | 193 |        * be a single string. It will return the data results in a hash
 | 
        
           |  |  | 194 |        * containing
 | 
        
           | 39 | rodolico | 195 |        *    'returnData'  - an array of array/hash/both depending on what you asked for
 | 
        
           | 13 | rodolico | 196 |        *    'count' - number of results (ie, count(data)
 | 
        
           |  |  | 197 |        *    'meta'  - metadata for each column returned
 | 
        
           |  |  | 198 |        *    'numfields' - number of columns in result (ie, count(meta))
 | 
        
           |  |  | 199 |        *    'errors' - normally empty array of errors
 | 
        
           |  |  | 200 |        * 
 | 
        
           |  |  | 201 |        * if the query modifies data (ie, NOT above), query may be an array
 | 
        
           |  |  | 202 |        * which will be surrounded by a transaction and rolled back if
 | 
        
           |  |  | 203 |        * anything causes an error.
 | 
        
           |  |  | 204 |        *
 | 
        
           |  |  | 205 |        * These will return a hash containing
 | 
        
           |  |  | 206 |        *    'count' l- number of rows affected by last statement
 | 
        
           |  |  | 207 |        *    'last_insert_id' - last insert id created by BLOCK of queries
 | 
        
           |  |  | 208 |        *    'errors' - normally empty array of errors which occurred (caused a rollback)
 | 
        
           |  |  | 209 |        * 
 | 
        
           |  |  | 210 |        */
 | 
        
           | 15 | rodolico | 211 |       public function doSQL( $query = null, $parameters = array() ) {
 | 
        
           | 13 | rodolico | 212 |          $errors = array();
 | 
        
           | 15 | rodolico | 213 |          if ( isset( $query ) ) {
 | 
        
           |  |  | 214 |             $this->parameters['query'] = $query;
 | 
        
           |  |  | 215 |          }
 | 
        
           | 13 | rodolico | 216 |          // if it is a "selectstatement" it doesn't modify data
 | 
        
           |  |  | 217 |          // if query is an array, assume it modifies something
 | 
        
           |  |  | 218 |          // if it is a single statement, look for the regex
 | 
        
           | 15 | rodolico | 219 |          $selectStatement = is_array( $this->parameters['query'] ) ?
 | 
        
           | 13 | rodolico | 220 |             false :
 | 
        
           | 29 | rodolico | 221 |             ( preg_match( '/^\s*(select)|(show)|(describe)|(explain)/xsi', $this->parameters['query'] ) === 1 );
 | 
        
           | 13 | rodolico | 222 |   | 
        
           |  |  | 223 |          // different actions based on whether it modifies data or not
 | 
        
           |  |  | 224 |          if ( $selectStatement ) { // if a select, simply return the rows
 | 
        
           |  |  | 225 |             // dataset is small enough, we just read it into memory all at one time.
 | 
        
           |  |  | 226 |             // NOTE: fetch_all is nice, but tied to mysqlnd, which has reports of problems, so we do it the old way
 | 
        
           | 15 | rodolico | 227 |             if ( $sth = parent::query( $this->parameters['query'], MYSQLI_USE_RESULT  ) ) {
 | 
        
           | 13 | rodolico | 228 |                if ( $sth === false ) {
 | 
        
           |  |  | 229 |                   $errors[] = array( 'id' => $this->errno, 'message' => $this->error );
 | 
        
           |  |  | 230 |                } else {
 | 
        
           |  |  | 231 |                   $this->parameters['columnMetaData'] = $sth->fetch_fields(); // get metadata
 | 
        
           |  |  | 232 |                   $this->parameters['returnData'] = array(); // we'll put all the results in an array
 | 
        
           |  |  | 233 |                   // $fetchtype returns either an array of array, array of hash, or both. Default is array of hash
 | 
        
           | 36 | rodolico | 234 |                   if ( isset( $this->parameters['returnType'] ) ) {
 | 
        
           |  |  | 235 |                      $this->parameters[ 'fetchType' ] = $this->parameters['returnType'] == 'array' ? MYSQLI_NUM : (
 | 
        
           |  |  | 236 |                            ( $this->parameters['returnType'] == 'both' ) ? MYSQLI_BOTH : MYSQLI_ASSOC
 | 
        
           | 13 | rodolico | 237 |                            );
 | 
        
           | 36 | rodolico | 238 |                   } else { // default is both (hash and numeric)
 | 
        
           |  |  | 239 |                      $this->parameters[ 'fetchType' ] =  MYSQLI_BOTH;
 | 
        
           |  |  | 240 |                      $this->parameters['returnType'] = 'both';
 | 
        
           | 13 | rodolico | 241 |                   }
 | 
        
           |  |  | 242 |                   // slurp all the stuff in
 | 
        
           |  |  | 243 |                   while ( $values =  $sth->fetch_array( $this->parameters[ 'fetchType' ] ) ) {
 | 
        
           | 38 | rodolico | 244 |                      $this->parameters['returnData'][] = $values;
 | 
        
           | 13 | rodolico | 245 |                   }
 | 
        
           |  |  | 246 |                   $sth->free(); // cleanup memory, don't need two copies
 | 
        
           |  |  | 247 |                } // if we had no errors
 | 
        
           | 1 | rodolico | 248 |             }
 | 
        
           | 13 | rodolico | 249 |             $this->parameters[ 'rowsAffected' ] = count( $this->parameters[ 'returnData' ] );
 | 
        
           |  |  | 250 |             $this->parameters[ 'numfields' ] = count( $this->parameters['columnMetaData'] );
 | 
        
           |  |  | 251 |             $this->parameters[ 'lastInsertKey' ] = 0;
 | 
        
           |  |  | 252 |             $this->parameters[ 'error' ] = $errors;
 | 
        
           |  |  | 253 |          } else {
 | 
        
           |  |  | 254 |             if ( ! is_array( $this->parameters['query'] ) ) { // not an array, so make it one
 | 
        
           |  |  | 255 |                $temp = $this->parameters['query'];
 | 
        
           |  |  | 256 |                $this->parameters['query'] = array( $temp );
 | 
        
           | 1 | rodolico | 257 |             }
 | 
        
           | 13 | rodolico | 258 |             // do it in a transaction so we can back out on failure
 | 
        
           |  |  | 259 |             $this->autocommit(false);
 | 
        
           |  |  | 260 |             $allOk = true;
 | 
        
           |  |  | 261 |             for ( $i = 0; $i < count( $this->parameters['query'] ); $i++ ) {
 | 
        
           | 15 | rodolico | 262 |                // debugging
 | 
        
           |  |  | 263 |                //print "$i\t" . $this->parameters['query'][$i] ."\n"; continue;
 | 
        
           |  |  | 264 |                // debugging
 | 
        
           | 13 | rodolico | 265 |                $this->logIt( isset( $parameters['username'] ) ? $parameters['username'] : 'unknown', $this->parameters['query'][$i] );
 | 
        
           |  |  | 266 |                if ( parent::query( $this->parameters['query'][$i] ) === false ) { // we had an erorr
 | 
        
           |  |  | 267 |                   // record it
 | 
        
           |  |  | 268 |                   $errors[] = array( 'id' => $this->errno, 'message' => $this->error, 'query' => $this->parameters['query'][$i] );
 | 
        
           |  |  | 269 |                   $allOk = false;
 | 
        
           |  |  | 270 |                   // and bail
 | 
        
           |  |  | 271 |                   break;
 | 
        
           |  |  | 272 |                }
 | 
        
           |  |  | 273 |             }
 | 
        
           |  |  | 274 |             // if we made it through ok, commit, otherwise rollback
 | 
        
           |  |  | 275 |             $allOk ? $this->commit() : $this->rollback();
 | 
        
           |  |  | 276 |             // reset autocommit to true
 | 
        
           |  |  | 277 |             $this->autocommit(true);
 | 
        
           |  |  | 278 |             $this->parameters['query'] = $query;
 | 
        
           |  |  | 279 |             $this->parameters[ 'rowsAffected' ] = $this->affected_rows;
 | 
        
           |  |  | 280 |             $this->parameters[ 'lastInsertKey' ] = $this->insert_id;
 | 
        
           |  |  | 281 |             $this->parameters[ 'error' ] = $errors;
 | 
        
           |  |  | 282 |          } // if select .. else
 | 
        
           |  |  | 283 |          return $this->parameters;
 | 
        
           |  |  | 284 |       }  // function doSQL
 | 
        
           | 17 | rodolico | 285 |   | 
        
           |  |  | 286 |       public function run () {
 | 
        
           |  |  | 287 |          return $this->doSQL( );
 | 
        
           |  |  | 288 |       }
 | 
        
           | 34 | rodolico | 289 |   | 
        
           |  |  | 290 |       /**
 | 
        
           | 40 | rodolico | 291 |        * Simple function returns last inserted id on succes, false on 
 | 
        
           |  |  | 292 |        * failure
 | 
        
           |  |  | 293 |        * 
 | 
        
           |  |  | 294 |        * @params string $query An insert query to execute
 | 
        
           |  |  | 295 |        * @return integer The insert id from this insert, if applicable
 | 
        
           |  |  | 296 |        */
 | 
        
           |  |  | 297 |   | 
        
           |  |  | 298 |       public function insert( $query ) {
 | 
        
           |  |  | 299 |          return $this->query( $query ) ? $this->insert_id : false;
 | 
        
           |  |  | 300 |       }
 | 
        
           |  |  | 301 |   | 
        
           |  |  | 302 |   | 
        
           |  |  | 303 |       /**
 | 
        
           | 34 | rodolico | 304 |        * returns an array of the first column for each row returned from query
 | 
        
           |  |  | 305 |        * 
 | 
        
           |  |  | 306 |        * The query is run, then for each row returns, the first column
 | 
        
           |  |  | 307 |        * is added to $return (an array). $return is then returned.
 | 
        
           |  |  | 308 |        * 
 | 
        
           |  |  | 309 |        * Used to do things like get an array of keyfields, or something
 | 
        
           |  |  | 310 |        * else.
 | 
        
           |  |  | 311 |        * 
 | 
        
           |  |  | 312 |        * @param string $query Query to run
 | 
        
           |  |  | 313 |        * @returns string[] Array of values
 | 
        
           |  |  | 314 |        */
 | 
        
           |  |  | 315 |       public function columnToArray( $query ) {
 | 
        
           |  |  | 316 |          $return = array();
 | 
        
           |  |  | 317 |          $result = $this->doSQL( $query );
 | 
        
           |  |  | 318 |          foreach ( $result['returnData'] as $row ) {
 | 
        
           |  |  | 319 |             $return[] = $row['id'];
 | 
        
           |  |  | 320 |          }
 | 
        
           |  |  | 321 |          return $return;
 | 
        
           |  |  | 322 |       }
 | 
        
           | 1 | rodolico | 323 |   | 
        
           |  |  | 324 |       /*
 | 
        
           |  |  | 325 |        * function will return one and only one row, NOT as an array of array
 | 
        
           |  |  | 326 |        * but as a single row array
 | 
        
           |  |  | 327 |        * if more than one row is returned by query, error is set and function
 | 
        
           |  |  | 328 |        * returns false.
 | 
        
           |  |  | 329 |        * Otherwise, function returns true
 | 
        
           |  |  | 330 |        */
 | 
        
           |  |  | 331 |       public function getOneRow( $sql = null ) {
 | 
        
           |  |  | 332 |          if ( isset( $sql ) )
 | 
        
           | 6 | rodolico | 333 |             $this->parameters[ 'query' ] = $sql;
 | 
        
           | 36 | rodolico | 334 |          $save = $this->parameters[ 'returnType' ];
 | 
        
           |  |  | 335 |          //print "<pre>" . print_r( $this->parameters['query'], true) . '</pre>';
 | 
        
           | 1 | rodolico | 336 |          $useAssociativeArray = true;
 | 
        
           |  |  | 337 |          $this->run();
 | 
        
           |  |  | 338 |          $useAssociativeArray = $save;
 | 
        
           | 6 | rodolico | 339 |          if ( $this->parameters[ 'rowsAffected' ] == 1 ) {
 | 
        
           |  |  | 340 |             $this->parameters[ 'returnData' ] = $this->parameters[ 'returnData' ][0];
 | 
        
           | 35 | rodolico | 341 |             return $this->parameters[ 'returnData' ];
 | 
        
           | 37 | rodolico | 342 |          } else {
 | 
        
           | 6 | rodolico | 343 |             $this->parameters['error'] = "$this->parameters[query] did not return a unique row in getOneRow";
 | 
        
           | 37 | rodolico | 344 |             print_r( $this->parameters['query'] ); die;
 | 
        
           |  |  | 345 |          }
 | 
        
           | 1 | rodolico | 346 |          return false;
 | 
        
           |  |  | 347 |       } // getOneRow
 | 
        
           |  |  | 348 |   | 
        
           |  |  | 349 |   | 
        
           |  |  | 350 |       // function returns the first column of the first row of data returned from query
 | 
        
           |  |  | 351 |       // or null no value returned
 | 
        
           | 6 | rodolico | 352 |       public function getOneDBValue( $sql = null ) {
 | 
        
           | 34 | rodolico | 353 |          //print '<pre>' . $sql . '</pre>';
 | 
        
           | 1 | rodolico | 354 |          if ( isset( $sql ) )
 | 
        
           | 6 | rodolico | 355 |             $this->parameters[ 'query' ] = $sql;
 | 
        
           | 36 | rodolico | 356 |          $save = $this->parameters['returnType'];
 | 
        
           |  |  | 357 |          $this->parameters['returnType'] = 'array';
 | 
        
           | 1 | rodolico | 358 |          $this->run();
 | 
        
           | 36 | rodolico | 359 |          $this->parameters['returnType'] = $save;
 | 
        
           |  |  | 360 |          //print "<pre>" . print_r($this->parameters,true ) . "</pre>";
 | 
        
           | 6 | rodolico | 361 |          return $this->parameters[ 'rowsAffected' ] ? $this->parameters[ 'returnData' ][0][0] : null;
 | 
        
           | 1 | rodolico | 362 |       }
 | 
        
           | 6 | rodolico | 363 |   | 
        
           | 4 | rodolico | 364 |       /*
 | 
        
           |  |  | 365 |        * function will attempt to make a constant ($value) safe for SQL depending on the type.
 | 
        
           |  |  | 366 |        * 
 | 
        
           |  |  | 367 |        * if $value is empty, $default is returned, as will happen if any of the
 | 
        
           |  |  | 368 |        * conversions (date, datetime, etc...) fail.
 | 
        
           |  |  | 369 |        * 
 | 
        
           |  |  | 370 |        * First, it will pass it through get_magic_quotes_gpc, 
 | 
        
           |  |  | 371 |        * then will run through mysql_real_escape_string
 | 
        
           |  |  | 372 |        * 
 | 
        
           |  |  | 373 |        * For strings, will encapsulate in quotes
 | 
        
           |  |  | 374 |        * Dates will attempt a conversion, then change to YYYY-MM-DD and encapsulate in quotes
 | 
        
           |  |  | 375 |        * DateTime will perform the same, but change to YYYY-MM-DD HH:MM:SS
 | 
        
           |  |  | 376 |        * Integer and Floats are passed through builtins intval and floatval
 | 
        
           |  |  | 377 |        * Boolean only checks the first character, a '0', 'f' and 'n' denoting false
 | 
        
           |  |  | 378 |        *    all else denoting true. The result is converted based on the variable
 | 
        
           |  |  | 379 |        *    $falsetrue, with the first char denoting false and the second denoting true
 | 
        
           |  |  | 380 |        */
 | 
        
           |  |  | 381 |       public static function makeSafeSQLConstant ( $value, $type='S', $default='null', $falsetrue='10' ) {
 | 
        
           |  |  | 382 |          if (strlen($value) == 0) // simply set any empty values to null
 | 
        
           |  |  | 383 |             return $default;
 | 
        
           |  |  | 384 |          // print "Processing $value as $type with default $default<br>\n";
 | 
        
           |  |  | 385 |          switch ( strtolower( $type ) ) {
 | 
        
           |  |  | 386 |             case 'string' :
 | 
        
           |  |  | 387 |             case 's' : 
 | 
        
           |  |  | 388 |                      if ( get_magic_quotes_gpc() ) 
 | 
        
           |  |  | 389 |                         $value = stripslashes($value);
 | 
        
           | 13 | rodolico | 390 |                      $value = $this->real_escape_string( $value );
 | 
        
           | 4 | rodolico | 391 |                      $value = strlen( $value ) > 0 ? "'$value'" : $default;
 | 
        
           |  |  | 392 |                      break;
 | 
        
           |  |  | 393 |             case 'date' :
 | 
        
           |  |  | 394 |             case 'd' :
 | 
        
           |  |  | 395 |                      if ( $value != 'null' ) {
 | 
        
           |  |  | 396 |                         $result = strtotime( $value );
 | 
        
           |  |  | 397 |                         $value = ( $result === false ) ? $default : "'" . Date( 'Y-m-d', $result) . "'";
 | 
        
           |  |  | 398 |                      }
 | 
        
           |  |  | 399 |                      break;
 | 
        
           |  |  | 400 |             case 'datetime':
 | 
        
           |  |  | 401 |             case 'timestamp':
 | 
        
           |  |  | 402 |             case 'dt': 
 | 
        
           |  |  | 403 |                      if ( $value != 'null' ) {
 | 
        
           |  |  | 404 |                         $result = strtotime( $value );
 | 
        
           |  |  | 405 |                         $value = ( $result === false ) ? $default : "'" . Date( 'Y-m-d H:i:s', $result) . "'";
 | 
        
           |  |  | 406 |                      }
 | 
        
           |  |  | 407 |                      break;
 | 
        
           |  |  | 408 |             case 'integer':
 | 
        
           |  |  | 409 |             case 'i' :  
 | 
        
           |  |  | 410 |                      $value = intval( $value );
 | 
        
           |  |  | 411 |                      break;
 | 
        
           |  |  | 412 |             case 'float':
 | 
        
           |  |  | 413 |             case 'f' :  
 | 
        
           |  |  | 414 |                      $value = floatval( $value );
 | 
        
           |  |  | 415 |                      break;
 | 
        
           |  |  | 416 |             case 'bool':
 | 
        
           |  |  | 417 |             case 'boolean':
 | 
        
           |  |  | 418 |             case 'b' :  // note, because of the way strpos works, you can not
 | 
        
           |  |  | 419 |                         // simply set $value based on the output; you MUST do
 | 
        
           |  |  | 420 |                         // as below; specifically check for false, then set the result
 | 
        
           |  |  | 421 |                         $value =  strpos( '0fn', strtolower(substr( $value, 0, 1 )) ) === false ? 0 : 1;
 | 
        
           |  |  | 422 |                         $value = substr( $falsetrue, $value, 0, 1 );
 | 
        
           |  |  | 423 |                         break;
 | 
        
           |  |  | 424 |          } // switch
 | 
        
           |  |  | 425 |          return $value;
 | 
        
           |  |  | 426 |       }
 | 
        
           | 1 | rodolico | 427 |   | 
        
           | 29 | rodolico | 428 |       /*
 | 
        
           |  |  | 429 |          '[^']*(?!\\)'(*SKIP)(*F)       # Make sure we're not matching inside of quotes
 | 
        
           |  |  | 430 |          |(?m-s:\s*(?:\-{2}|\#)[^\n]*$) # Single line comment
 | 
        
           |  |  | 431 |          |(?:
 | 
        
           |  |  | 432 |            \/\*.*?\*\/                  # Multi-line comment
 | 
        
           |  |  | 433 |            (?(?=(?m-s:\h+$))         # Get trailing whitespace if any exists and only if it's the rest of the line
 | 
        
           |  |  | 434 |              \h+
 | 
        
           |  |  | 435 |            )
 | 
        
           |  |  | 436 |          )
 | 
        
           |  |  | 437 |       */
 | 
        
           |  |  | 438 |       /**
 | 
        
           |  |  | 439 |        * Functions strips SQL queries from a file. Above is a commented copy of the regex used
 | 
        
           |  |  | 440 |        * 
 | 
        
           |  |  | 441 |        * @param string $query An arbitrary sized SQL script
 | 
        
           |  |  | 442 |        * @returns string $query, with all SQL comments removed
 | 
        
           |  |  | 443 |        */
 | 
        
           | 4 | rodolico | 444 |   | 
        
           | 29 | rodolico | 445 |       public function strip_sqlcomment ($query = '') {
 | 
        
           |  |  | 446 |          $regex = '\'[^\']*(?!\\\)\'(*SKIP)(*F)|(?m-s:\\s*(?:\\-{2}|\\#)[^\\n]*$)|(?:\\/\\*.*?\\*\\/(?(?=(?m-s:\h+$))\\h+))';
 | 
        
           |  |  | 447 |          //print "<pre>$regex</pre>" ; die;
 | 
        
           |  |  | 448 |           return (($query == '') ?  '' : preg_replace( "/$regex/xs", '', $query ));
 | 
        
           |  |  | 449 |       }
 | 
        
           |  |  | 450 |   | 
        
           |  |  | 451 |       /**
 | 
        
           |  |  | 452 |        * runs an SQL script with multiple statements in it
 | 
        
           |  |  | 453 |        * 
 | 
        
           | 37 | rodolico | 454 |        * If $sql is an array, it is assumed each row is a separate query
 | 
        
           |  |  | 455 |        * 
 | 
        
           |  |  | 456 |        * If $sql is a string, will separate it into different lines and
 | 
        
           |  |  | 457 |        * execute them
 | 
        
           |  |  | 458 |        * 
 | 
        
           | 29 | rodolico | 459 |        * NOTE: it does this by breaking it based on the semicolon, so
 | 
        
           |  |  | 460 |        * in some weird situations, it will break at the wrong place.
 | 
        
           |  |  | 461 |        * 
 | 
        
           |  |  | 462 |        */
 | 
        
           |  |  | 463 |       public function runSQLScript( $sql ) {
 | 
        
           | 37 | rodolico | 464 |          if ( is_array( $sql ) ) {
 | 
        
           |  |  | 465 |             $queries = $sql;
 | 
        
           |  |  | 466 |          } else {
 | 
        
           |  |  | 467 |             $sql = $this->strip_sqlcomment( $sql );
 | 
        
           |  |  | 468 |             $queries = explode( ";", $sql );
 | 
        
           |  |  | 469 |          }
 | 
        
           | 29 | rodolico | 470 |          //print "<pre>" . print_r( $queries, true ) . '</pre>'; die;
 | 
        
           |  |  | 471 |          foreach ( $queries as $query ) {
 | 
        
           |  |  | 472 |             $this->doSQL( trim( implode( ' ', explode("\n",$query ) ) ) );
 | 
        
           |  |  | 473 |          }
 | 
        
           | 37 | rodolico | 474 |       } // runSQLScript
 | 
        
           |  |  | 475 |   | 
        
           |  |  | 476 |       /**
 | 
        
           |  |  | 477 |        * Overrides real_escape_string to change behaviour slightly
 | 
        
           |  |  | 478 |        * 
 | 
        
           |  |  | 479 |        * Will check if string is pure numeric and, if it is, will return
 | 
        
           |  |  | 480 |        * as it is. Otherwise, will call real_escape_string, then wrap
 | 
        
           |  |  | 481 |        * result in single quotes
 | 
        
           |  |  | 482 |        */
 | 
        
           |  |  | 483 |        public function my_escape_string( $string, $additionalEscapes = array() ) {
 | 
        
           |  |  | 484 |           if ( is_numeric( $string ) )
 | 
        
           |  |  | 485 |              return $string;
 | 
        
           |  |  | 486 |           $string = $this->real_escape_string( $string );
 | 
        
           |  |  | 487 |           if ( count( $additionalEscapes ) ) 
 | 
        
           |  |  | 488 |              $string = addcslashes( $string, implode( '', $additionalEscapes ) );
 | 
        
           |  |  | 489 |           return "'$string'";
 | 
        
           |  |  | 490 |        }
 | 
        
           |  |  | 491 |   | 
        
           |  |  | 492 |       /**
 | 
        
           |  |  | 493 |        * Builds a query of form update $tablename set $fields where $where
 | 
        
           |  |  | 494 |        * 
 | 
        
           |  |  | 495 |        * Creates a query that will update table $tablename. It assumes
 | 
        
           |  |  | 496 |        * $fields is an array where the indexes are fieldnames and the values
 | 
        
           |  |  | 497 |        * are the new values for the field. Will escape the values.
 | 
        
           |  |  | 498 |        * 
 | 
        
           |  |  | 499 |        * Appends $where, again where index is a field name
 | 
        
           |  |  | 500 |        */
 | 
        
           |  |  | 501 |       public function updateQuery( $tablename, $where, $fields ) {
 | 
        
           |  |  | 502 |          /*
 | 
        
           |  |  | 503 |          print "<pre>updateQuery\n\ntable\n$tablename\n</pre>";
 | 
        
           |  |  | 504 |          print "<pre>\nwhere\n" . print_r( $where, true ) . "\n</pre>";
 | 
        
           |  |  | 505 |          print "<pre>fields\n" . print_r( $fields, true ) . "\n</pre>"; die;
 | 
        
           |  |  | 506 |          */
 | 
        
           |  |  | 507 |          $sql = '';
 | 
        
           |  |  | 508 |          $updateFields = array();
 | 
        
           |  |  | 509 |          foreach ( $fields as $fieldname => $value ) {
 | 
        
           |  |  | 510 |             $updateFields[] = sprintf( "%s = %s", $fieldname, $this->my_escape_string( $value ) );
 | 
        
           |  |  | 511 |          }
 | 
        
           |  |  | 512 |          $sql = "update $tablename set " . implode( ", ", $updateFields );
 | 
        
           |  |  | 513 |          $updateFields = array();
 | 
        
           |  |  | 514 |          foreach ( $where as $fieldname => $value ) {
 | 
        
           |  |  | 515 |             $updateFields[] = sprintf( "%s = %s", $fieldname, $this->my_escape_string( $value ) );
 | 
        
           |  |  | 516 |          }
 | 
        
           |  |  | 517 |          if ( count( $updateFields ) ) {
 | 
        
           |  |  | 518 |             $sql .= ' where ' . implode( ' and ', $updateFields );
 | 
        
           |  |  | 519 |          }
 | 
        
           |  |  | 520 |          return $sql;
 | 
        
           |  |  | 521 |       }
 | 
        
           |  |  | 522 |   | 
        
           |  |  | 523 |       /**
 | 
        
           |  |  | 524 |        * Creates an insert query from $fields
 | 
        
           |  |  | 525 |        * 
 | 
        
           |  |  | 526 |        */
 | 
        
           |  |  | 527 |        public function insertQuery( $tablename, $fields ) {
 | 
        
           | 40 | rodolico | 528 |           //print "<pre>insertQuery\n\ntable\n$tablename\n</pre>";
 | 
        
           |  |  | 529 |           //print "<pre>fields\n" . print_r( $fields, true ) . "\n</pre>"; die;
 | 
        
           | 37 | rodolico | 530 |           $query = "insert into $tablename (" . implode( ',',array_keys($fields) );
 | 
        
           | 40 | rodolico | 531 |           $query .= ") values (" . implode( ',', array_map( array($this, 'my_escape_string'), array_values( $fields ) ) );
 | 
        
           | 37 | rodolico | 532 |           return "$query)";
 | 
        
           |  |  | 533 |        } // insertQuery
 | 
        
           |  |  | 534 |   | 
        
           | 29 | rodolico | 535 |   | 
        
           | 1 | rodolico | 536 |    } // class DBQuery
 | 
        
           |  |  | 537 |   | 
        
           | 17 | rodolico | 538 | /*
 | 
        
           |  |  | 539 |  *    $db = new DBQuery( '127.0.0.1', 'camp', 'camp', 'camp' );
 | 
        
           | 13 | rodolico | 540 |   | 
        
           |  |  | 541 |    if ($db->connect_error) {
 | 
        
           |  |  | 542 |        die('Connect Error (' . $db->connect_errno . ') '  . $db->connect_error);
 | 
        
           |  |  | 543 |    }
 | 
        
           |  |  | 544 |    $result = $db->doSQL(
 | 
        
           |  |  | 545 |                array(
 | 
        
           |  |  | 546 |                      'drop table if exists temp',
 | 
        
           |  |  | 547 |                      'create table temp ( col1 int unsigned )',
 | 
        
           |  |  | 548 |                      "insert into temp values ('mike')"
 | 
        
           |  |  | 549 |                      )
 | 
        
           |  |  | 550 |          );
 | 
        
           | 15 | rodolico | 551 |    if ( $result['error'] ) {
 | 
        
           | 13 | rodolico | 552 |       print_r ( $result );
 | 
        
           | 15 | rodolico | 553 |       die ( DBQuery::error2String( $result['error'] ) );
 | 
        
           | 13 | rodolico | 554 |    } else {
 | 
        
           |  |  | 555 |       print "running select\n";
 | 
        
           |  |  | 556 |       $result = $db->doSQL( 'select * from temp' );
 | 
        
           |  |  | 557 |       print_r( $result );
 | 
        
           |  |  | 558 |    }
 | 
        
           |  |  | 559 | //   $return = $db->doSQL( "select device.device_id 'id',device.name 'name', device_type.name 'type' from device join device_type using (device_type_id) where device.removed_date is null and device_type.show_as_system = 'Y'" );
 | 
        
           |  |  | 560 | //   print_r( $return );
 | 
        
           |  |  | 561 | //   print_r( $db );
 | 
        
           | 17 | rodolico | 562 | */
 | 
        
           | 1 | rodolico | 563 | ?>
 | 
        
           | 13 | rodolico | 564 |   | 
        
           |  |  | 565 |   | 
        
           |  |  | 566 |   | 
        
           |  |  | 567 |   | 
        
           |  |  | 568 |   | 
        
           |  |  | 569 |   | 
        
           |  |  | 570 |   | 
        
           |  |  | 571 |   | 
        
           |  |  | 572 |   | 
        
           |  |  | 573 |   |