PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/common/UniqueIDGenerator.php

https://bitbucket.org/wildanm/orangehrm
PHP | 359 lines | 239 code | 43 blank | 77 comment | 31 complexity | 892be263aaafa6d68df8ef40d25001fa MD5 | raw file
Possible License(s): CC-BY-SA-3.0, AGPL-3.0, BSD-3-Clause, AGPL-1.0, GPL-2.0, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * OrangeHRM is a comprehensive Human Resource Management (HRM) System that captures
  4. * all the essential functionalities required for any enterprise.
  5. * Copyright (C) 2006 OrangeHRM Inc., http://www.orangehrm.com
  6. *
  7. * OrangeHRM is free software; you can redistribute it and/or modify it under the terms of
  8. * the GNU General Public License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * OrangeHRM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  12. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with this program;
  16. * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17. * Boston, MA 02110-1301, USA
  18. *
  19. */
  20. /* Check if running through upgrader and skip including if so */
  21. $confPHP = ROOT_PATH . '/lib/confs/Conf.php';
  22. if(file_exists($confPHP)) {
  23. @require_once ROOT_PATH . '/lib/dao/DMLFunctions.php';
  24. }
  25. /**
  26. * Class to generate unique incrementing ID's.
  27. * Implemented as a singleton.
  28. */
  29. class UniqueIDGenerator {
  30. const TABLE_NAME = "hs_hr_unique_id";
  31. const INCREMENT_ID_SQL = "UPDATE hs_hr_unique_id SET last_id = LAST_INSERT_ID(last_id + 1) WHERE table_name = '%s' AND field_name = '%s'";
  32. const GET_ID_SQL = "SELECT LAST_INSERT_ID()";
  33. const SELECT_SQL = "SELECT last_id FROM hs_hr_unique_id WHERE table_name = '%s' AND field_name = '%s'";
  34. const INSERT_SQL = "INSERT INTO hs_hr_unique_id(table_name, field_name, last_id) VALUES('%s', '%s', %d)";
  35. const UPDATE_SQL = "UPDATE hs_hr_unique_id SET last_id = %s WHERE table_name = '%s' AND field_name = '%s'";
  36. const FIND_INVALID_ID_SQL = "SELECT COUNT(*) FROM %s WHERE %s NOT LIKE '%s%%'";
  37. const FIND_EXISTING_MAX_ID_SQL = "SELECT MAX(%s) FROM %s";
  38. const RESET_SQL = "UPDATE hs_hr_unique_id SET last_id = 0";
  39. /** This singleton instance */
  40. private static $instance;
  41. /**
  42. * Private constructor
  43. */
  44. private function __construct() {
  45. }
  46. /**
  47. * Get the singleton instance of this class
  48. */
  49. public static function getInstance() {
  50. if (!is_object(self::$instance)) {
  51. self::$instance = new UniqueIDGenerator();
  52. }
  53. return self::$instance;
  54. }
  55. /**
  56. * Get the next unique ID for the given table and field
  57. * If $prefix is specified, uses that when generating the ID
  58. * eg: $prefix = "NAT" gives ID's like "NAT013".
  59. *
  60. * @param string $tableName The table Name
  61. * @param string $fieldName The field Name
  62. * @param string $prefix Optional prefix used in the ID
  63. * @param int $minNumLength Minimum width of the numeric part if the ID.
  64. * If the number is less than this width, it is
  65. * left padded with zeros. (only used when a $prefix is given)
  66. *
  67. * @return mixed The next ID.
  68. */
  69. public function getNextID($tableName, $fieldName, $prefix = null, $minNumWidth = 3) {
  70. $updateSql = sprintf(self::INCREMENT_ID_SQL, strtolower($tableName), strtolower($fieldName));
  71. $dbConnection = new DMLFunctions();
  72. $result = $dbConnection->executeQuery($updateSql);
  73. if (!$result || (mysql_affected_rows() == 0)) {
  74. throw new IDGeneratorException("Error getting next ID for $tableName, $fieldName");
  75. }
  76. $result = $dbConnection->executeQuery(self::GET_ID_SQL);
  77. if (!$result) {
  78. throw new IDGeneratorException("Error in query for last inserted ID for $tableName, $fieldName");
  79. }
  80. $row = mysql_fetch_array($result, MYSQL_NUM);
  81. if (empty($row)) {
  82. throw new IDGeneratorException("Error getting last inserted ID for $tableName, $fieldName");
  83. }
  84. /* LAST_INSERT_ID always returns a value */
  85. $nextId = $row[0];
  86. if (!empty($prefix)) {
  87. $nextId = $prefix . str_pad($nextId, $minNumWidth, "0", STR_PAD_LEFT);
  88. }
  89. return $nextId;
  90. }
  91. /**
  92. * Get the last highest unique ID for the given table and field
  93. * If $prefix is specified, uses that when generating the ID
  94. * eg: $prefix = "NAT" gives ID's like "NAT013".
  95. *
  96. * @param string $tableName The table Name
  97. * @param string $fieldName The field Name
  98. * @param string $prefix Optional prefix used in the ID
  99. * @param int $minNumLength Minimum width of the numeric part if the ID.
  100. * If the number is less than this width, it is
  101. * left padded with zeros. (only used when a $prefix is given)
  102. *
  103. * @return mixed The last ID.
  104. */
  105. public function getLastID($tableName, $fieldName, $prefix = null, $minNumWidth = 3) {
  106. /* Get existing lastId value */
  107. $sql = sprintf(self::SELECT_SQL, $tableName, $fieldName);
  108. $dbConnection = new DMLFunctions();
  109. $result = $dbConnection->executeQuery($sql);
  110. if ($result && (mysql_num_rows($result) == 1)) {
  111. $row = mysql_fetch_array($result, MYSQL_NUM);
  112. $lastId = $row[0];
  113. if (!empty($prefix)) {
  114. $lastId = $prefix . str_pad($lastId, $minNumWidth, "0", STR_PAD_LEFT);
  115. }
  116. return $lastId;
  117. } else {
  118. throw new IDGeneratorException("Error querying last ID. SQL = $sql");
  119. }
  120. }
  121. /**
  122. * Resets unique ID's to lowest possible values.
  123. * Calling this method will lose all previous ID values and reset
  124. * them to the max of existing ID's.
  125. */
  126. public function resetIDs() {
  127. $sql = self::RESET_SQL;
  128. $dbConnection = new DMLFunctions();
  129. $result = $dbConnection->executeQuery($sql);
  130. if (!$result) {
  131. throw new IDGeneratorException("Error resetting id's to zero. = $sql");
  132. }
  133. $this->initTable();
  134. }
  135. /**
  136. * Initializes the unique ID table by checking for tables with
  137. * incorrect ID values and resetting them. Could be run after a
  138. * database upgrade to reset the ID values if needed.
  139. *
  140. * If $link is given, uses that mysql link identifier, otherwise uses
  141. * the DMLFunctions class to access the database.
  142. *
  143. * @param resource $link mysql link identifier
  144. */
  145. public function initTable($link = null) {
  146. $idFields = array(
  147. new IDField("hs_hr_membership", "membship_code", "MME"),
  148. new IDField("hs_hr_membership_type", "membtype_code", "MEM"),
  149. new IDField("hs_hr_nationality", "nat_code", "NAT"),
  150. new IDField("hs_hr_ethnic_race", "ethnic_race_code", "ETH"),
  151. new IDField("hs_hr_skill", "skill_code", "SKI"),
  152. new IDField("hs_hr_language", "lang_code", "LAN"),
  153. new IDField("hs_hr_education", "edu_code", "EDU"),
  154. new IDField("hs_hr_licenses", "licenses_code", "LIC"),
  155. new IDField("hs_hr_job_title", "jobtit_code", "JOB"),
  156. new IDField("hs_pr_salary_grade", "sal_grd_code", "SAL"),
  157. new IDField("hs_hr_empstat", "estat_code", "EST"),
  158. new IDField("hs_hr_eec", "eec_code", "EEC"),
  159. new IDField("hs_hr_location", "loc_code", "LOC"),
  160. new IDField("hs_hr_compstructtree", "id"),
  161. new IDField("hs_hr_users", "id", "USR"),
  162. new IDField("hs_hr_user_group", "userg_id", "USG"),
  163. new IDField("hs_hr_customer", "customer_id"),
  164. new IDField("hs_hr_project", "project_id"),
  165. new IDField("hs_hr_project_activity", "activity_id"),
  166. new IDField("hs_hr_module", "mod_id", "MOD"),
  167. new IDField("hs_hr_empreport", "rep_code", "REP"),
  168. new IDField("hs_hr_employee", "emp_number"),
  169. new IDField("hs_hr_leave", "leave_id"),
  170. new IDField("hs_hr_holidays", "holiday_id"),
  171. new IDField("hs_hr_leavetype", "leave_type_id", "LTY"),
  172. new IDField("hs_hr_leave_requests", "leave_request_id"),
  173. new IDField("hs_hr_time_event", "time_event_id"),
  174. new IDField("hs_hr_timesheet", "timesheet_id"),
  175. new IDField("hs_hr_workshift", "workshift_id"),
  176. new IDField("hs_hr_custom_export", "export_id"),
  177. new IDField("hs_hr_custom_import", "import_id"),
  178. new IDField("hs_hr_job_spec", "jobspec_id"),
  179. new IDField("hs_hr_job_vacancy", "vacancy_id"),
  180. new IDField("hs_hr_job_application", "application_id"),
  181. new IDField("hs_hr_job_application_events", "id"),
  182. /* Not used yet. Uncomment when we start using these
  183. new IDField("hs_hr_employee_timesheet_period", "timesheet_period_id"),
  184. new IDField("hs_hr_timesheet_submission_period", "timesheet_period_id"),
  185. */
  186. );
  187. foreach ($idFields as $idField) {
  188. $insert = false;
  189. $lastId = 0;
  190. $tableName = $idField->getTableName();
  191. $fieldName = $idField->getFieldName();
  192. $prefix = $idField->getPrefix();
  193. /* Get existing lastId value */
  194. $sql = sprintf(self::SELECT_SQL, $tableName, $fieldName);
  195. $result = (empty($link)) ? mysql_query($sql) : mysql_query($sql, $link);
  196. if (!$result) {
  197. $errMsg = (empty($link)) ? mysql_error() : mysql_error($link);
  198. throw new IDGeneratorException("Error querying last ID. SQL = $sql. Msg = $errMsg");
  199. }
  200. $numRows = mysql_num_rows($result);
  201. if ($numRows === 1) {
  202. $insert = false;
  203. $row = mysql_fetch_array($result, MYSQL_NUM);
  204. $lastId = $row[0];
  205. } else if ($numRows === 0) {
  206. $insert = true;
  207. } else {
  208. $errMsg = (empty($link)) ? mysql_error() : mysql_error($link);
  209. throw new IDGeneratorException("Error in hs_hr_unique_id table. Msg = $errMsg");
  210. }
  211. /* If the field has a prefix, look for existing invalid id's */
  212. if (!empty($prefix)) {
  213. $sql = sprintf(self::FIND_INVALID_ID_SQL, $tableName, $fieldName, $prefix);
  214. $result = (empty($link)) ? mysql_query($sql) : mysql_query($sql, $link);
  215. if (!$result) {
  216. $errMsg = (empty($link)) ? mysql_error() : mysql_error($link);
  217. throw new IDGeneratorException("Error looking for invalid ID's. SQL = $sql. Msg = $errMsg");
  218. }
  219. $row = mysql_fetch_array($result, MYSQL_NUM);
  220. if (empty($row)) {
  221. throw new IDGeneratorException("Error fetching num_rows. SQL = $sql");
  222. }
  223. if ($row[0] > 0) {
  224. throw new IDGeneratorException("Invalid ID's in table=$tableName, Field=$fieldName. SQL=$sql");
  225. }
  226. }
  227. /* Get existing maximum ID from the table */
  228. $sql = sprintf(self::FIND_EXISTING_MAX_ID_SQL, $fieldName, $tableName);
  229. $result = (empty($link)) ? mysql_query($sql) : mysql_query($sql, $link);
  230. if (!$result) {
  231. $errMsg = (empty($link)) ? mysql_error() : mysql_error($link);
  232. throw new IDGeneratorException("Error looking for existing MAX ID. SQL = $sql. Msg = $errMsg");
  233. }
  234. $row = mysql_fetch_array($result, MYSQL_NUM);
  235. if (empty($row)) {
  236. throw new IDGeneratorException("Error looking for existing MAX ID. SQL = $sql");
  237. }
  238. $existingMax = $row[0];
  239. if (!empty($existingMax)) {
  240. if (!empty($prefix)) {
  241. /* Remove prefix and covert to int */
  242. $existingMax = str_replace($prefix, "", $existingMax);
  243. $existingMax = intVal($existingMax);
  244. }
  245. if ($existingMax > $lastId) {
  246. $lastId = $existingMax;
  247. }
  248. }
  249. if ($insert) {
  250. $sql = sprintf(self::INSERT_SQL, $tableName, $fieldName, $lastId);
  251. } else {
  252. $sql = sprintf(self::UPDATE_SQL, $lastId, $tableName, $fieldName);
  253. }
  254. $result = (empty($link)) ? mysql_query($sql) : mysql_query($sql, $link);
  255. if (!$result) {
  256. $errMsg = (empty($link)) ? mysql_error() : mysql_error($link);
  257. throw new IDGeneratorException("Error updating hs_hr_unique_id table. SQL = $sql. Msg = $errMsg");
  258. }
  259. }
  260. }
  261. }
  262. /**
  263. * Class representing one unique ID field
  264. */
  265. class IDField {
  266. protected $tableName;
  267. protected $fieldName;
  268. protected $prefix;
  269. public function getTableName() {
  270. return $this->tableName;
  271. }
  272. public function getFieldName() {
  273. return $this->fieldName;
  274. }
  275. public function getPrefix() {
  276. return $this->prefix;
  277. }
  278. /**
  279. * Constructor
  280. *
  281. * @param string $table Name of the table
  282. * @param string $field ID Field
  283. * @param string $prefix Optional prefix used in the ID.
  284. */
  285. public function __construct($table, $field, $prefix = null) {
  286. $this->tableName = $table;
  287. $this->fieldName = $field;
  288. $this->prefix = $prefix;
  289. }
  290. }
  291. class IDGeneratorException extends Exception {
  292. }
  293. ?>