/library/filerepository/backend/FileRepository_Backend_MySQL.class.php

https://github.com/cj/Project-Pier · PHP · 423 lines · 333 code · 18 blank · 72 comment · 20 complexity · 840f10a2bc0e3c94fc48eb5fc9fc2b19 MD5 · raw file

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