/** * Generate a pseudo-random password with letters, numbers, and symbols. * * @param int $length * @return string * @uses Helpers::is_valid_int() */ public static function generateRandom($length = 64) { // input validation if (!Helpers::is_valid_int($length, true)) { return ''; } // generate a pseudo-random password $password = ''; // these are used in the for loop $char_types = array('lower_letters' => 'abcdefghijklmnopqrstuvwxyz', 'upper_letters' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'numbers' => '0123456789', 'symbols' => '`~!@#$%^*()-_=+[{]}|:,.?'); $types = array_keys($char_types); $max_same = 3; $last_type = -1; $same_type = 0; for ($i = 0; $i < $length; $i++) { // determine the type of the character $type_index = rand(0, count($types) - 1); $type = $char_types[$types[$type_index]]; if ($last_type === $type_index) { $same_type++; } else { $same_type = 0; } if ($same_type > 2) { $i--; continue; } // determine how many characters to use for type $num_same = rand(1, $max_same); for ($j = 0; $j < $num_same; $j++) { $password .= substr(${$type}, rand(0, strlen(${$type}) - 1), 1); if ($j > 0) { $i++; } } $last_type = $type_index; if (strlen($password) >= $length) { break; } } return substr($password, 0, $length); }
/** * Build constants from values in database and store them in a PHP file. Require the file at the end of execution. * * @return bool * @uses Constants::getQuery() * @uses Generator::setIterator() * @uses Generator::buildTopContent() * @uses Constants::generate() * @uses Constants::write() */ public function build() { $this->Log->write(__METHOD__, Log::LOG_LEVEL_SYSTEM_INFORMATION); // get constant list $this->Log->write('getting constant list', Log::LOG_LEVEL_USER); $result = $this->getQuery(); if (!Helpers::is_array_ne($result)) { $this->Log->write('could not get constant list', Log::LOG_LEVEL_WARNING); return false; } list($sql, $params) = $result; // set the iterator from the SQL and parameters $set_iterator = $this->setIterator($sql, $params); if (!$set_iterator) { $this->Log->write('could not set iterator with SQL and params', Log::LOG_LEVEL_WARNING, $result); } $this->Log->write('have constant list', Log::LOG_LEVEL_USER); // build the top content of the PHP file $size = $this->buildTopContent(__CLASS__); if (!Helpers::is_valid_int($size) || $size < 1) { $this->Log->write('could not write top content', Log::LOG_LEVEL_WARNING, __CLASS__); return false; } // write after each call to generate to decrease memory consumption on server foreach ($this->iterator as $i => $row) { $generated = $this->generate($row); if ($generated === false) { $this->Log->write($i . ': Failed to generate code from row', Log::LOG_LEVEL_WARNING); continue; } $bytes = $this->write(); if ($bytes === false) { $this->Log->write($i . ': could not write PHP to file', Log::LOG_LEVEL_WARNING); } } // require the generated file to provide constants to the rest of the application require_once $this->file_path; return true; }
/** * Update a table based on key/value pairs and WHERE parameters * * @param string $table Table to update * @param array $pairs Key/value pairs to SET * @param array $where Where values * @param int $limit Limit value (only positive integers will be used for limiting) * @param bool $enqueue Enqueue or execute the SQL * @return bool|mixed * @uses Db::buildUpdate() * @uses Db::where() * @uses Db::begin() * @uses Db::query() * @uses Db::commit() */ public function update($table = '', $pairs = array(), $where = array(), $limit = -1, $enqueue = false) { $this->Log->write(__METHOD__, Log::LOG_LEVEL_SYSTEM_INFORMATION); // input validation if (!Helpers::is_string_ne($table) || !Helpers::is_array_ne($pairs)) { $this->Log->write('table OR pairs is empty', Log::LOG_LEVEL_WARNING); return false; } if (!Helpers::is_array_ne($where)) { $this->Log->write('there is no where clause', Log::LOG_LEVEL_WARNING); // there might be a problem: we will update everything // TODO: determine the best way to handle no WHERE clause return false; } // build SQL list($sql, $params) = $this->buildUpdate($table, $pairs); // handle WHERE list($wsql, $wparams) = $this->where($where); $sql .= $wsql; $params = array_merge($params, $wparams); // handle LIMIT if (Helpers::is_valid_int($limit, true)) { $sql .= PHP_EOL . ' LIMIT ' . $limit; } if ($enqueue) { $this->Log->write('enqueue sql', Log::LOG_LEVEL_SYSTEM_INFORMATION); return $this->enqueue($sql, $params, 'update'); } else { $this->Log->write('updating sql in transaction', Log::LOG_LEVEL_SYSTEM_INFORMATION); // execute UPDATE query in transaction $this->begin(); $updated = $this->query($sql, $params, 'update'); $this->commit(); $this->Log->write('committed transaction for update', Log::LOG_LEVEL_USER); return $updated; } }
/** * Return error message, based on upload error status. * * @param int $error * @return bool|string */ private function handleError($error) { $this->Log->write(__METHOD__, Log::LOG_LEVEL_SYSTEM_INFORMATION); if (!Helpers::is_valid_int($error, true)) { $this->Log->write('Cannot handle an error that is not a positive integer.', Log::LOG_LEVEL_WARNING); return false; } switch ($error) { case UPLOAD_ERR_INI_SIZE: $message = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.'; break; case UPLOAD_ERR_FORM_SIZE: $message = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.'; break; case UPLOAD_ERR_PARTIAL: $message = 'The uploaded file was only partially uploaded.'; break; case UPLOAD_ERR_NO_FILE: $message = 'No file was uploaded.'; break; case UPLOAD_ERR_NO_TMP_DIR: $message = 'Missing a temporary folder.'; break; case UPLOAD_ERR_CANT_WRITE: $message = 'Failed to write file to disk.'; break; case UPLOAD_ERR_EXTENSION: $message = 'A PHP extension stopped the file upload.'; break; default: $message = 'Unknown upload error (' . Helpers::get_string($error) . ')'; break; } $this->Log->write('Error: ' . $message, Log::LOG_LEVEL_ERROR); return $message; }
/** * Get the user from the database and store in a property. * * @return array|bool|mixed */ private function getUser() { $this->Log->write(__METHOD__, Log::LOG_LEVEL_SYSTEM_INFORMATION); // use cached version if (Helpers::is_array_ne($this->user_data)) { return $this->user_data; } // determine which field and value to use in the query if (Helpers::is_valid_int($this->id, true)) { $where_field = $this->id_field; $value = $this->id; } elseif (Helpers::is_string_ne($this->name)) { $where_field = $this->name_field; $value = $this->name; } elseif (array_key_exists('user_id', $_SESSION)) { $where_field = $this->id_field; $value = $_SESSION['user_id']; } elseif (array_key_exists('user_name', $_SESSION)) { $where_field = $this->name_field; $value = $_SESSION['user_name']; } else { $this->Log->write('cannot determine user data to use', Log::LOG_LEVEL_WARNING); return false; } // build query $sql = 'SELECT *' . PHP_EOL; $sql .= ' FROM ' . $this->table . PHP_EOL; $sql .= ' WHERE ' . $where_field . ' = ?'; $row = $this->query($sql, array($value), 'first'); if (!Helpers::is_array_ne($row)) { return false; } $this->user_data = $row; $this->id = $row['id']; $this->name = $row['name']; return $row; }
/** * Set and/or get the log level. * * [@param] int $log_level Optional log level value * @return int */ public function logLevel() { // set the log level if the parameter is valid $args = func_get_args(); if (count($args) === 1) { if (Helpers::is_valid_int($args[0]) && $this->validateLevel($args[0])) { $this->log_level = $args[0]; } else { $this->write('invalid log level {' . $args[0] . '}', Log::LOG_LEVEL_WARNING); } } // return the log level return $this->log_level; }
/** * Removes duplicate rows (based on UNIQUE key), tracks duplicated IDs, and maps duplicate IDs to unique IDs. * * @param string $table To table name * @param array $rows All rows from the table that need to be inserted * @return bool * @uses Db::getKeyField() * @uses $GLOBALS['table_relationships'] * @uses DatabaseMap::$unique_fields * @uses Db::getIdFromName() * @see Relationship::build() */ private function fixDuplicateIds($table = '', &$rows = array()) { $this->Log->write(__METHOD__, Log::LOG_LEVEL_SYSTEM_INFORMATION); // input validation if (!Helpers::is_string_ne($table)) { $this->Log->write('table is invalid', Log::LOG_LEVEL_WARNING, $table); return false; } if (!Helpers::is_array_ne($rows)) { $this->Log->write('array is invalid', Log::LOG_LEVEL_WARNING, $rows); return false; } // get this table's unique fields $unique_fields = $this->getKeyField($table, 'unique'); // prepare unique array based on $unique_fields (which might be empty) $unique = array(); foreach ($unique_fields as $field) { $unique[$field] = array(); } // get relationships for $table as child table if (array_key_exists($table, $GLOBALS['table_relationships'])) { $relation = $GLOBALS['table_relationships'][$table]; $result = $this->buildTableFieldValues($relation); if ($result === false) { $this->Log->write('Could not get values from relations', Log::LOG_LEVEL_USER, $relation); } elseif ($result === 0) { $this->Log->write('Found no values from relations', Log::LOG_LEVEL_USER, $relation); } else { $this->Log->write('Found values', Log::LOG_LEVEL_USER, $result); } } // fix the rows foreach ($rows as $index => &$row) { // check for duplicate rows foreach ($unique_fields as $field) { $value = $row[$field]; $existing_id = $this->getIdFromName($table, $value, $field); if (Helpers::is_valid_int($existing_id) && $existing_id > 0) { // consider unique values already in the database table $unique[$field][$value][] = $existing_id; unset($rows[$index]); continue 2; } elseif (array_key_exists($value, $unique[$field])) { // remove rows that would duplicate unique values $unique[$field][$value][] = $row['id']; unset($rows[$index]); continue 2; } else { // this is the first instance of this value, so build a list for future mapping of IDs $unique[$field][$value] = array(); } } // check relationships in order to change parent IDs in rows to use actual parent ID values (after removing duplicates) if (isset($relation)) { // loop through relations to find parent tables for the child fields foreach ($relation as $field => $array) { // make sure the child field exists in the row if (!array_key_exists($field, $row)) { continue; } // make sure this parent table exists in unique fields $relation_table = $relation[$field]['table']; if (!array_key_exists($relation_table, $this->unique_fields)) { continue; } // check for the current row's parent ID in the unique_fields for the parent table $current_id = $row[$field]; $use_value = null; $use_field = null; foreach ($this->unique_fields[$relation_table] as $unique_field => $values) { foreach ($values as $value => $ids) { // get the field and value used for this parent ID to use later if (in_array($current_id, $ids)) { $use_value = $value; $use_field = $unique_field; break 2; } } } // make sure there is a value to use to proceed if ($use_value === null) { continue; } // get the actual parent ID for the field and value that were found earlier $use_id = $this->getIdFromName($relation[$field]['table'], $use_value, $use_field); if (!Helpers::is_valid_int($use_id) || $use_id === 0) { $this->Log->write('could not get ID from ' . $relation[$field]['table'] . ' for ' . $use_value, Log::LOG_LEVEL_WARNING); continue; } // update the row to use the actual parent ID for the value (like find and replace) $row[$field] = $use_id; // check for existence of key field value before allowing it to be inserted (in order to avoid foreign // key constraint violations) and generate a report of the rows that would have failed if (!in_array($use_id, $this->table_field_values[$array['table']][$array['field']])) { $this->missing_keys[$array['table']][$array['field']][] = $use_id; $this->missing_keys[$array['table']][$array['field']] = array_unique($this->missing_keys[$array['table']][$array['field']]); sort($this->missing_keys[$array['table']][$array['field']]); //unset($rows[$index]); } } } } // sort by either first field, id field, or specified field in parameters usort($rows, function ($a, $b) { if (array_key_exists('id', $a) && array_key_exists('id', $b)) { $id_field = 'id'; $id_field_a = $id_field; $id_field_b = $id_field; } else { // get first key $keys = array_keys($a); $id_field_a = $keys[0]; $keys = array_keys($b); $id_field_b = $keys[0]; } return $a[$id_field_a] > $b[$id_field_b]; }); // save any values from $unique to the unique fields array to use in the loop above $this->unique_fields[$table] = $unique; return true; }
/** * Set and/or get the log level. * * @return int * @uses Log::validateLevel() */ public function logLevel() { $args = func_get_args(); if (Helpers::is_array_ne($args)) { if (Helpers::is_valid_int($args[0]) && (is_object($this->Log) && $this->Log->validateLevel($args[0]))) { $this->log_level = $args[0]; $this->Log->logLevel($this->log_level); } } return $this->log_level; }
/** * Get the last successful login attempt from the database. * * @param int $user_id * @return bool|mixed */ public function lastSuccessful($user_id = 0) { $this->Log->write(__METHOD__, Log::LOG_LEVEL_SYSTEM_INFORMATION); if (!Helpers::is_valid_int($user_id, true)) { $this->Log->write('invalid user id', Log::LOG_LEVEL_WARNING, $user_id); return false; } $sql = 'SELECT *' . PHP_EOL; $sql .= ' FROM ' . $this->table . PHP_EOL; $sql .= ' WHERE user_id = ?' . PHP_EOL; $sql .= ' AND success = 1' . PHP_EOL; $sql .= ' ORDER BY add_date DESC' . PHP_EOL; $sql .= ' LIMIT 1'; $row = $this->query($sql, array($user_id), 'single'); $this->Log->write('found row', Log::LOG_LEVEL_USER, $row); return $row; }
/** * Convert a text file (with appropriate file extension [docx, doc, odt, txt]) to a PDF file. * This uses Libre Office, a command line utility, to convert the file in the file system and requires exec permission. * * @return bool|mixed * @uses Convert::$input_file * @uses Convert::replaceExtension() */ private function textToPdf() { if (!is_file($this->input_file)) { $this->Log->write('Input file is not part of this file system. Please specify the correct file with Convert::inputFile() or Convert::uploadFile().', Log::LOG_LEVEL_WARNING); return false; } $command = '/usr/bin/soffice --headless --convert-to pdf ' . $this->input_file; exec($command, $output, $return_var); if (Helpers::is_valid_int($return_var) && $return_var != 0) { $this->Log->write('An error occurred when executing command:' . PHP_EOL . implode(PHP_EOL, $output), Log::LOG_LEVEL_WARNING); return false; } $output_file = $this->replaceExtension('pdf'); return $output_file; }