PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/application/plugins/files/library/backend/FileRepository_Backend_MySQL.class.php

https://github.com/fb83/Project-Pier
PHP | 443 lines | 353 code | 18 blank | 72 comment | 20 complexity | 7bb7685a2c3c365adaacca53cf24ba33 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, AGPL-3.0, LGPL-2.1, GPL-3.0
  1. <?php
  2. /**
  3. * File repository backend that put files into the MySQL database
  4. *
  5. * @package FileRepository.backend
  6. * @http://www.projectpier.org/
  7. */
  8. class FileRepository_Backend_MySQL implements FileRepository_Backend {
  9. /** Names of database tables, prefix will be added in front of them **/
  10. const FILES_TABLE = 'file_repo';
  11. const ATTRIBUTES_TABLE = 'file_repo_attributes';
  12. /**
  13. * Database connection resource
  14. *
  15. * @var resource
  16. */
  17. private $db_link;
  18. /**
  19. * Table prefix
  20. *
  21. * @var string
  22. */
  23. private $table_prefix;
  24. /**
  25. * Constructor
  26. *
  27. * @param resource $db_link Databse connection resource
  28. * @param string $table_prefix
  29. * @return FileRepository_Backend_MySQL
  30. */
  31. function __construct($db_link, $table_prefix) {
  32. trace(__FILE__, '__construct()');
  33. $this->setDbLink($db_link);
  34. $this->setTablePrefix($table_prefix);
  35. } // __construct
  36. // ---------------------------------------------------
  37. // FileRepository_Backend implementation
  38. // ---------------------------------------------------
  39. /**
  40. * Return array of all files in repository
  41. *
  42. * @param void
  43. * @return null
  44. */
  45. function listFiles() {
  46. $files_table = $this->getFilesTableName();
  47. if ($result = mysql_query("SELECT `id` FROM $files_table ORDER BY `order`", $this->db_link)) {
  48. $ids = array();
  49. while ($row = mysql_fetch_assoc($result)) {
  50. $ids[] = $row['id'];
  51. } // while
  52. return $ids;
  53. } // if
  54. return array();
  55. } // listFiles
  56. /**
  57. * Return number of files in repository
  58. *
  59. * @param void
  60. * @return integer
  61. */
  62. function countFiles() {
  63. $files_table = $this->getFilesTableName();
  64. if ($result = mysql_query("SELECT COUNT(`id`) AS 'row_count' FROM $files_table", $this->db_link)) {
  65. if ($row = mysql_fetch_assoc($result)) {
  66. return (integer) $row['row_count'];
  67. } // if
  68. } // if
  69. return 0;
  70. } // countFiles
  71. /**
  72. * Read the content of the file and return it
  73. *
  74. * @param string $file_id
  75. * @return string
  76. */
  77. function getFileContent($file_id) {
  78. if (!$this->isInRepository($file_id)) {
  79. throw new FileNotInRepositoryError($file_id);
  80. } // if
  81. $files_table = $this->getFilesTableName();
  82. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  83. if ($result = mysql_query("SELECT `content` FROM $files_table WHERE `id` = '$escaped_id'", $this->db_link)) {
  84. if ($row = mysql_fetch_assoc($result)) {
  85. return $row['content'];
  86. } // if
  87. } // if
  88. return null;
  89. } // getFileContent
  90. /**
  91. * Return all file attributes for specific file. If file has no attributes empty array is
  92. * returned
  93. *
  94. * @param string $file_id
  95. * @return array
  96. * @throws FileNotInRepositoryError
  97. */
  98. function getFileAttributes($file_id) {
  99. if (!$this->isInRepository($file_id)) {
  100. throw new FileNotInRepositoryError($file_id);
  101. } // if
  102. $attributes_table = $this->getAttributesTableName();
  103. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  104. if ($result = mysql_query("SELECT `attribute`, `value` FROM $attributes_table WHERE `id` = '$escaped_id'", $this->db_link)) {
  105. $attributes = array();
  106. while ($row = mysql_fetch_assoc($result)) {
  107. $attributes[$row['attribute']] = eval($row['value']);
  108. } // while
  109. return $attributes;
  110. } // if
  111. return array();
  112. } // getFileAttributes
  113. /**
  114. * Return value of specific file attribute
  115. *
  116. * @param string $file_id
  117. * @param string $attribute_name
  118. * @param mixed $default Default value is returned when attribute is not found
  119. * @return mixed
  120. * @throws FileNotInRepositoryError if file is not in repository
  121. */
  122. function getFileAttribute($file_id, $attribute_name, $default = null) {
  123. if (!$this->isInRepository($file_id)) {
  124. throw new FileNotInRepositoryError($file_id);
  125. } // if
  126. $attributes_table = $this->getAttributesTableName();
  127. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  128. $escaped_attribute = mysql_real_escape_string($attribute_name, $this->db_link);
  129. if ($result = mysql_query("SELECT `value` FROM $attributes_table WHERE `id` = '$escaped_id' AND `attribute` = '$escaped_attribute'", $this->db_link)) {
  130. if ($row = mysql_fetch_assoc($result)) {
  131. return eval($row['value']);
  132. } // if
  133. } // if
  134. return $default;
  135. } // getFileAttribute
  136. /**
  137. * Set attribute value for specific file
  138. *
  139. * @param string $file_id
  140. * @param string $attribute_name
  141. * @param mixed $attribute_value Objects and resources are not supported. Scalars and arrays are
  142. * @return null
  143. * @throws FileNotInRepositoryError If $file_id does not exists in repository
  144. * @throws InvalidParamError If we have an object or a resource as attribute value
  145. */
  146. function setFileAttribute($file_id, $attribute_name, $attribute_value) {
  147. if (!$this->isInRepository($file_id)) {
  148. throw new FileNotInRepositoryError($file_id);
  149. } // if
  150. if (is_object($attribute_value) || is_resource($attribute_value)) {
  151. throw new InvalidParamError('$attribute_value', $attribute_value, 'Objects and resources are not supported as attribute values');
  152. } // if
  153. $attributes_table = $this->getAttributesTableName();
  154. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  155. $escaped_attribute = mysql_real_escape_string($attribute_name, $this->db_link);
  156. $escaped_value = mysql_real_escape_string('return ' . var_export($attribute_value, true) . ';', $this->db_link);
  157. if ($result = mysql_query("SELECT `value` FROM $attributes_table WHERE `id` = '$escaped_id' AND `attribute` = '$escaped_attribute'", $this->db_link)) {
  158. if (mysql_num_rows($result) == 0) {
  159. mysql_query("INSERT INTO $attributes_table (`id`, `attribute`, `value`) VALUES ('$escaped_id', '$escaped_attribute', '$escaped_value')", $this->db_link);
  160. } else {
  161. mysql_query("UPDATE $attributes_table SET `value` = '$escaped_value' WHERE `id` = '$escaped_id' AND `attribute` = '$escaped_attribute''", $this->db_link);
  162. } // if
  163. } // if
  164. } // setFileAttribute
  165. /**
  166. * Add file to the repository
  167. *
  168. * @param string $source Path of the source file
  169. * @param array $attributes Array of file attributes
  170. * @return string File ID
  171. * @throws FileDnxError if source is not readable
  172. * @throws FailedToCreateFolderError if we fail to create subdirectory
  173. * @throws FileRepositoryAddError if we fail to move file to the repository
  174. */
  175. function addFile($source, $attributes = null) {
  176. if (!is_readable($source)) {
  177. throw new FileDnxError($source);
  178. } // if
  179. $file_id = $this->getUniqueId();
  180. $files_table = $this->getFilesTableName();
  181. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  182. $order = intval($this->getNextOrder());
  183. mysql_query("BEGIN WORK", $this->db_link);
  184. $success = true;
  185. $seq = 0;
  186. $block_size = 500 * 1024; // 500 KB
  187. $handle = fopen($source, "rb");
  188. while (!feof($handle)) {
  189. $seq++;
  190. $data = fread($handle, $block_size);
  191. $escaped_content = mysql_real_escape_string($data, $this->db_link);
  192. if (!mysql_query("INSERT INTO $files_table (`id`, `seq`, `content`, `order`) VALUES ('$escaped_id', $seq, '$escaped_content', $order)", $this->db_link)) {
  193. echo mysql_error();
  194. $success = false;
  195. break;
  196. }
  197. }
  198. fclose($handle);
  199. if ($success) {
  200. mysql_query("COMMIT", $this->db_link);
  201. if (is_array($attributes)) {
  202. foreach ($attributes as $attribute_name => $attribute_value) {
  203. $this->setFileAttribute($file_id, $attribute_name, $attribute_value);
  204. } // foreach
  205. } // if
  206. return $file_id;
  207. } else {
  208. mysql_query("ROLLBACK", $this->db_link);
  209. throw new FileRepositoryAddError($source, $file_id);
  210. } // if
  211. } // addFile
  212. /**
  213. * Update content of specific file
  214. *
  215. * @param string $file_id
  216. * @param string $source
  217. * @return boolean
  218. * @throws FileDnxError if source file is not readable
  219. * @throws FileNotInRepositoryError if $file_id is not in the repository
  220. * @throws FileRepositoryAddError if we fail to update file
  221. */
  222. function updateFileContent($file_id, $source) {
  223. if (!is_readable($source)) {
  224. throw new FileDnxError($source);
  225. } // if
  226. if (!$this->isInRepository($file_id)) {
  227. throw new FileNotInRepositoryError($file_id);
  228. } // if
  229. $files_table = $this->getFilesTableName();
  230. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  231. $escaped_content = mysql_real_escape_string(file_get_contents($source), $this->db_link);
  232. if (mysql_query("UPDATE $files_table SET `content` = '$escaped_content' WHERE `id` = '$escaped_id'", $this->db_link)) {
  233. return true;
  234. } else {
  235. throw new FileRepositoryAddError($source, $file_id);
  236. } // if
  237. } // updateFileContent
  238. /**
  239. * Delete file from the repository
  240. *
  241. * @param string $file_id
  242. * @return boolean
  243. * @throws FileNotInRepositoryError if $file_id is not in the repository
  244. * @throws FileRepositoryDeleteError if we fail to delete file
  245. */
  246. function deleteFile($file_id) {
  247. if (!$this->isInRepository($file_id)) {
  248. throw new FileNotInRepositoryError($file_id);
  249. } // if
  250. $files_table = $this->getFilesTableName();
  251. $attributes_table = $this->getAttributesTableName();
  252. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  253. mysql_query("BEGIN WORK", $this->db_link);
  254. if (!mysql_query("DELETE FROM $files_table WHERE `id` = '$escaped_id'", $this->db_link)) {
  255. throw new FileRepositoryDeleteError($file);
  256. } // if
  257. if (!mysql_query("DELETE FROM $attributes_table WHERE `id` = '$escaped_id'", $this->db_link)) {
  258. mysql_query('ROLLBACK', $this->db_link);
  259. throw new FileRepositoryDeleteError($file);
  260. } // if
  261. return mysql_query("COMMIT", $this->db_link);
  262. } // deleteFile
  263. /**
  264. * Drop all files from repository
  265. *
  266. * @param void
  267. * @return null
  268. */
  269. function cleanUp() {
  270. $files_table = $this->getFilesTableName();
  271. $attributes_table = $this->getAttributesTableName();
  272. mysql_query("BEGIN WORK", $this->db_link);
  273. mysql_query("DELETE FROM $files_table", $this->db_link);
  274. mysql_query("DELETE FROM $attributes_table", $this->db_link);
  275. mysql_query("COMMIT", $this->db_link);
  276. } // cleanUp
  277. /**
  278. * Check if specific file is in repository
  279. *
  280. * @param string $file_id
  281. * @return boolean
  282. */
  283. function isInRepository($file_id) {
  284. $files_table = $this->getFilesTableName();
  285. $escaped_id = mysql_real_escape_string($file_id, $this->db_link);
  286. if ($result = mysql_query("SELECT COUNT(`id`) AS 'row_count' FROM $files_table WHERE `id` = '$escaped_id'", $this->db_link)) {
  287. if ($row = mysql_fetch_assoc($result)) {
  288. return (boolean) $row['row_count'];
  289. } // if
  290. } // if
  291. return false;
  292. } // isInRepository
  293. // ---------------------------------------------------
  294. // Utils
  295. // ---------------------------------------------------
  296. /**
  297. * Return files table name
  298. *
  299. * @param boolean $escape Escape table name
  300. * @return string
  301. */
  302. protected function getFilesTableName($escape = true) {
  303. $table_name = $this->getTablePrefix() . self::FILES_TABLE;
  304. return $escape ? '`' . $table_name . '`' : $table_name;
  305. } // getFilesTableName
  306. /**
  307. * Return attributes table name
  308. *
  309. * @param boolean $escape Escape table name
  310. * @return string
  311. */
  312. protected function getAttributesTableName($escape = true) {
  313. $table_name = $this->getTablePrefix() . self::ATTRIBUTES_TABLE;
  314. return $escape ? '`' . $table_name . '`' : $table_name;
  315. } // getAttributesTableName
  316. /**
  317. * Return unique file ID
  318. *
  319. * @param void
  320. * @return string
  321. */
  322. protected function getUniqueId() {
  323. $files_table = $this->getFilesTableName();
  324. do {
  325. $id = sha1(uniqid(rand(), true));
  326. $escaped_id = mysql_real_escape_string($id, $this->db_link);
  327. if ($result = mysql_query("SELECT COUNT(`id`) AS 'row_count' FROM $files_table WHERE `id` = '$escaped_id'", $this->db_link)) {
  328. $row = mysql_fetch_assoc($result);
  329. if (!is_array($row) || !isset($row['row_count'])) $row['row_count'] = 0;
  330. } else {
  331. $row['row_count'] = 0;
  332. } // if
  333. } while ($row['row_count'] > 0);
  334. return $id;
  335. } // getUniqueId
  336. /**
  337. * Return next order
  338. *
  339. * @param void
  340. * @return integer
  341. */
  342. protected function getNextOrder() {
  343. $files_table = $this->getFilesTableName();
  344. if ($result = mysql_query("SELECT max(`order`) as `order` FROM $files_table", $this->db_link)) {
  345. if ($row = mysql_fetch_assoc($result)) {
  346. return (integer) $row['order'] + 1;
  347. } // if
  348. } // if
  349. return 1;
  350. } // getNextOrder
  351. // ---------------------------------------------------
  352. // Getters and setters
  353. // ---------------------------------------------------
  354. /**
  355. * Get db_link
  356. *
  357. * @param null
  358. * @return resource
  359. */
  360. function getDbLink() {
  361. return $this->db_link;
  362. } // getDbLink
  363. /**
  364. * Set db_link value
  365. *
  366. * @param resource $value
  367. * @return null
  368. */
  369. function setDbLink($value) {
  370. trace(__FILE__, '__setDbLink()');
  371. if (!is_resource($value) || (strpos(get_resource_type($value), 'mysql') === false)) {
  372. throw new InvalidParamError('value', $value, 'DB link need to be MySQL connection resouce');
  373. } // if
  374. $this->db_link = $value;
  375. } // setDbLink
  376. /**
  377. * Get table_prefix
  378. *
  379. * @param null
  380. * @return string
  381. */
  382. function getTablePrefix() {
  383. return $this->table_prefix;
  384. } // getTablePrefix
  385. /**
  386. * Set table_prefix value
  387. *
  388. * @param string $value
  389. * @return null
  390. */
  391. function setTablePrefix($value) {
  392. $this->table_prefix = $value;
  393. } // setTablePrefix
  394. } // FileRepository_Backend_MySQL
  395. ?>