HookManagement.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * @TODO: Improve description
  5. *
  6. * @package chamilo.hookmanagement
  7. */
  8. class HookManagement implements HookManagementInterface
  9. {
  10. /**
  11. * Constructor.
  12. */
  13. protected function __construct()
  14. {
  15. $this->tables[TABLE_HOOK_OBSERVER] = Database::get_main_table(TABLE_HOOK_OBSERVER);
  16. $this->tables[TABLE_HOOK_EVENT] = Database::get_main_table(TABLE_HOOK_EVENT);
  17. $this->tables[TABLE_HOOK_CALL] = Database::get_main_table(TABLE_HOOK_CALL);
  18. $this->hookCalls = $this->listAllHookCalls();
  19. $this->hookEvents = $this->listAllHookEvents();
  20. $this->hookObservers = $this->listAllHookObservers();
  21. }
  22. /**
  23. * Instance the hook manager.
  24. *
  25. * @staticvar null $result
  26. *
  27. * @return HookManagement
  28. */
  29. public static function create()
  30. {
  31. static $result = null;
  32. return $result ? $result : $result = new self();
  33. }
  34. /**
  35. * Insert hook into Database. Return insert id.
  36. *
  37. * @param string $eventName
  38. * @param string $observerClassName
  39. * @param int $type
  40. *
  41. * @return int
  42. */
  43. public function insertHook($eventName, $observerClassName, $type)
  44. {
  45. if ($type === HOOK_EVENT_TYPE_ALL) {
  46. $this->insertHook($eventName, $observerClassName, HOOK_EVENT_TYPE_PRE);
  47. $this->insertHook($eventName, $observerClassName, HOOK_EVENT_TYPE_POST);
  48. } else {
  49. $this->insertHookIfNotExist($eventName, $observerClassName);
  50. // Check if exists hook call
  51. $row = Database::select(
  52. 'id, enabled',
  53. $this->tables[TABLE_HOOK_CALL],
  54. [
  55. 'where' => [
  56. 'hook_event_id = ? ' => $this->hookEvents[$eventName],
  57. 'AND hook_observer_id = ? ' => $this->hookObservers[$observerClassName],
  58. 'AND type = ? ' => $type,
  59. ],
  60. ],
  61. 'ASSOC'
  62. );
  63. if (!empty($row) && is_array($row)) {
  64. // Check if is hook call is active
  65. if ((int) $row['enabled'] === 0) {
  66. Database::update(
  67. $this->tables[TABLE_HOOK_CALL],
  68. [
  69. 'enabled' => 1,
  70. ],
  71. [
  72. 'id = ?' => $row['id'],
  73. ]
  74. );
  75. }
  76. }
  77. }
  78. }
  79. /**
  80. * Delete hook from Database. Return deleted rows number.
  81. *
  82. * @param string $eventName
  83. * @param string $observerClassName
  84. * @param int $type
  85. *
  86. * @return int
  87. */
  88. public function deleteHook($eventName, $observerClassName, $type)
  89. {
  90. if ($type === HOOK_EVENT_TYPE_ALL) {
  91. $this->deleteHook($eventName, $observerClassName, HOOK_EVENT_TYPE_PRE);
  92. $this->deleteHook($eventName, $observerClassName, HOOK_EVENT_TYPE_POST);
  93. } else {
  94. $this->insertHookIfNotExist($eventName, $observerClassName);
  95. Database::update(
  96. $this->tables[TABLE_HOOK_CALL],
  97. [
  98. 'enabled' => 0,
  99. ],
  100. [
  101. 'id = ? ' => $this->hookCalls[$eventName][$observerClassName][$type],
  102. ]
  103. );
  104. }
  105. }
  106. /**
  107. * Update hook observer order by hook event.
  108. *
  109. * @param $eventName
  110. * @param $type
  111. * @param $hookOrders
  112. *
  113. * @return int
  114. */
  115. public function orderHook($eventName, $type, $hookOrders)
  116. {
  117. foreach ($this->hookCalls[$eventName] as $observerClassName => $types) {
  118. foreach ($hookOrders as $oldOrder => $newOrder) {
  119. $res = Database::update(
  120. $this->tables[TABLE_HOOK_CALL],
  121. [
  122. 'hook_order ' => $newOrder,
  123. ],
  124. [
  125. 'id = ? ' => $types[$type],
  126. 'AND hook_order = ? ' => $oldOrder,
  127. ]
  128. );
  129. if ($res) {
  130. break;
  131. }
  132. }
  133. }
  134. }
  135. /**
  136. * Return a list an associative array where keys are the active hook observer class name.
  137. *
  138. * @param string $eventName
  139. *
  140. * @return array
  141. */
  142. public function listHookObservers($eventName)
  143. {
  144. $array = [];
  145. $joinTable = $this->tables[TABLE_HOOK_CALL].' hc'.
  146. ' INNER JOIN '.$this->tables[TABLE_HOOK_EVENT].' he'.
  147. ' ON hc.hook_event_id = he.id '.
  148. ' INNER JOIN '.$this->tables[TABLE_HOOK_OBSERVER].' ho '.
  149. ' ON hc.hook_observer_id = ho.id ';
  150. $columns = 'ho.class_name, ho.path, ho.plugin_name, hc.enabled';
  151. $where = ['where' => ['he.class_name = ? ' => $eventName, 'AND hc.enabled = ? ' => 1]];
  152. $rows = Database::select($columns, $joinTable, $where);
  153. foreach ($rows as $row) {
  154. $array[$row['class_name']] = $row;
  155. }
  156. return $array;
  157. }
  158. /**
  159. * Return a list an associative array where keys are the active hook observer class name.
  160. *
  161. * @return array
  162. */
  163. public function listAllHookObservers()
  164. {
  165. $array = [];
  166. $columns = 'id, class_name';
  167. $rows = Database::select($columns, $this->tables[TABLE_HOOK_OBSERVER]);
  168. foreach ($rows as $row) {
  169. $array[$row['class_name']] = $row['id'];
  170. }
  171. return $array;
  172. }
  173. /**
  174. * Return a list an associative array where keys are the active hook observer class name.
  175. *
  176. * @return array
  177. */
  178. public function listAllHookEvents()
  179. {
  180. $array = [];
  181. $columns = 'id, class_name';
  182. $rows = Database::select($columns, $this->tables[TABLE_HOOK_EVENT]);
  183. foreach ($rows as $row) {
  184. $array[$row['class_name']] = $row['id'];
  185. }
  186. return $array;
  187. }
  188. /**
  189. * Return a list an associative array where keys are the active hook observer class name.
  190. *
  191. * @return array
  192. */
  193. public function listAllHookCalls()
  194. {
  195. $array = [];
  196. $joinTable = $this->tables[TABLE_HOOK_CALL].' hc'.
  197. ' INNER JOIN '.$this->tables[TABLE_HOOK_EVENT].' he'.
  198. ' ON hc.hook_event_id = he.id '.
  199. ' INNER JOIN '.$this->tables[TABLE_HOOK_OBSERVER].' ho '.
  200. ' ON hc.hook_observer_id = ho.id ';
  201. $columns = 'he.class_name AS event_class_name, ho.class_name AS observer_class_name, hc.id AS id, hc.type AS type';
  202. $rows = Database::select($columns, $joinTable);
  203. foreach ($rows as $row) {
  204. $array[$row['event_class_name']][$row['observer_class_name']][$row['type']] = $row['id'];
  205. }
  206. return $array;
  207. }
  208. /**
  209. * Check if hooks (event, observer, call) exist in Database, if not,
  210. * Will insert them into their respective table.
  211. *
  212. * @param string $eventName
  213. * @param string $observerClassName
  214. *
  215. * @return int
  216. */
  217. public function insertHookIfNotExist($eventName = null, $observerClassName = null)
  218. {
  219. // Check if exists hook event
  220. if (isset($eventName) && !isset($this->hookEvents[$eventName])) {
  221. $attributes = [
  222. 'class_name' => $eventName,
  223. 'description' => get_lang('HookDescription'.$eventName),
  224. ];
  225. $id = Database::insert($this->tables[TABLE_HOOK_EVENT], $attributes);
  226. $this->hookEvents[$eventName] = $id;
  227. }
  228. // Check if exists hook observer
  229. if (isset($observerClassName) &&
  230. !isset($this->hookObservers[$observerClassName])
  231. ) {
  232. $object = $observerClassName::create();
  233. $attributes = [
  234. 'class_name' => $observerClassName,
  235. 'path' => $object->getPath(),
  236. 'plugin_name' => $object->getPluginName(),
  237. ];
  238. $id = Database::insert($this->tables[TABLE_HOOK_OBSERVER], $attributes);
  239. $this->hookObservers[$observerClassName] = $id;
  240. }
  241. if (isset($eventName) &&
  242. isset($observerClassName) &&
  243. !isset($this->hookCalls[$eventName][$observerClassName])
  244. ) {
  245. // HOOK TYPE PRE
  246. $row = Database::select(
  247. 'MAX(hook_order) as hook_order',
  248. $this->tables[TABLE_HOOK_CALL],
  249. [
  250. 'where' => [
  251. 'hook_event_id = ? ' => $this->hookEvents[$eventName],
  252. 'AND type = ? ' => HOOK_EVENT_TYPE_PRE,
  253. ],
  254. ],
  255. 'ASSOC'
  256. );
  257. // Check if exists hook call
  258. $id = Database::insert(
  259. $this->tables[TABLE_HOOK_CALL],
  260. [
  261. 'hook_event_id' => $this->hookEvents[$eventName],
  262. 'hook_observer_id' => $this->hookObservers[$observerClassName],
  263. 'type' => HOOK_EVENT_TYPE_PRE,
  264. 'enabled' => 0,
  265. 'hook_order' => $row['hook_order'] + 1,
  266. ]
  267. );
  268. $this->hookCalls[$eventName][$observerClassName][HOOK_EVENT_TYPE_PRE] = $id;
  269. // HOOK TYPE POST
  270. $row = Database::select(
  271. 'MAX(hook_order) as hook_order',
  272. $this->tables[TABLE_HOOK_CALL],
  273. [
  274. 'where' => [
  275. 'hook_event_id = ? ' => $this->hookEvents[$eventName],
  276. 'AND type = ? ' => HOOK_EVENT_TYPE_POST,
  277. ],
  278. ],
  279. 'ASSOC'
  280. );
  281. // Check if exists hook call
  282. $id = Database::insert(
  283. $this->tables[TABLE_HOOK_CALL],
  284. [
  285. 'hook_event_id' => $this->hookEvents[$eventName],
  286. 'hook_observer_id' => $this->hookObservers[$observerClassName],
  287. 'type' => HOOK_EVENT_TYPE_POST,
  288. 'enabled' => 0,
  289. 'hook_order' => $row['hook_order'] + 1,
  290. ]
  291. );
  292. $this->hookCalls[$eventName][$observerClassName][HOOK_EVENT_TYPE_POST] = $id;
  293. } elseif (isset($eventName) && !isset($observerClassName)) {
  294. foreach ($this->hookObservers as $observer => $id) {
  295. $this->insertHookIfNotExist($eventName, $observer);
  296. }
  297. } elseif (!isset($eventName) && isset($observerClassName)) {
  298. foreach ($this->hookEvents as $event => $id) {
  299. $this->insertHookIfNotExist($event, $observerClassName);
  300. }
  301. }
  302. return 1;
  303. }
  304. /**
  305. * Return the hook call id identified by hook event, hook observer and type.
  306. *
  307. * @param string $eventName
  308. * @param string $observerClassName
  309. * @param int $type
  310. *
  311. * @return mixed
  312. */
  313. public function getHookCallId($eventName, $observerClassName, $type)
  314. {
  315. $eventName = Database::escape_string($eventName);
  316. $observerClassName($observerClassName);
  317. $type = Database::escape_string($type);
  318. $joinTable = $this->tables[TABLE_HOOK_CALL].' hc'.
  319. ' INNER JOIN '.$this->tables[TABLE_HOOK_EVENT].' he'.
  320. ' ON hc.hook_event_id = he.id '.
  321. ' INNER JOIN '.$this->tables[TABLE_HOOK_OBSERVER].' ho '.
  322. ' ON hc.hook_observer_id = ho.id ';
  323. $row = Database::select(
  324. 'id',
  325. $joinTable,
  326. [
  327. 'where' => [
  328. 'he.class_name = ? ' => $eventName,
  329. 'AND ho.class_name = ? ' => $observerClassName,
  330. 'AND hc.type = ? ' => $type,
  331. ],
  332. ],
  333. 'ASSOC'
  334. );
  335. return $row['id'];
  336. }
  337. }