Inspector.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <?php
  2. namespace Elgg\Debug;
  3. use Elgg\Debug\Inspector\ViewComponent;
  4. /**
  5. * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
  6. *
  7. * @access private
  8. *
  9. * @package Elgg.Core
  10. * @since 1.11
  11. */
  12. class Inspector {
  13. /**
  14. * Get Elgg event information
  15. *
  16. * @return array [event,type] => array(handlers)
  17. */
  18. public function getEvents() {
  19. return $this->buildHandlerTree(_elgg_services()->events->getAllHandlers());
  20. }
  21. /**
  22. * Get Elgg plugin hooks information
  23. *
  24. * @return array [hook,type] => array(handlers)
  25. */
  26. public function getPluginHooks() {
  27. return $this->buildHandlerTree(_elgg_services()->hooks->getAllHandlers());
  28. }
  29. /**
  30. * Get all view types for known views
  31. *
  32. * @return string[]
  33. */
  34. public function getViewtypes() {
  35. global $CONFIG;
  36. return array_keys($CONFIG->views->locations);
  37. }
  38. /**
  39. * Get Elgg view information
  40. *
  41. * @param string $viewtype The Viewtype we wish to inspect
  42. *
  43. * @return array [view] => map of priority to ViewComponent[]
  44. */
  45. public function getViews($viewtype = 'default') {
  46. global $CONFIG;
  47. $overrides = null;
  48. if ($CONFIG->system_cache_enabled) {
  49. $data = _elgg_services()->systemCache->load('view_overrides');
  50. if ($data) {
  51. $overrides = unserialize($data);
  52. }
  53. } else {
  54. $overrides = _elgg_services()->views->getOverriddenLocations();
  55. }
  56. // maps view name to array of ViewComponent[] with priority as keys
  57. $views = array();
  58. $location = "{$CONFIG->viewpath}{$viewtype}/";
  59. $core_file_list = $this->recurseFileTree($location);
  60. // setup views array before adding extensions and plugin views
  61. foreach ($core_file_list as $path) {
  62. $component = ViewComponent::fromPaths($path, $location);
  63. $views[$component->view] = array(500 => $component);
  64. }
  65. // add plugins and handle overrides
  66. foreach ($CONFIG->views->locations[$viewtype] as $view => $location) {
  67. $component = new ViewComponent();
  68. $component->view = $view;
  69. $component->location = "{$location}{$viewtype}/";
  70. $views[$view] = array(500 => $component);
  71. }
  72. // now extensions
  73. foreach ($CONFIG->views->extensions as $view => $extensions) {
  74. $view_list = array();
  75. foreach ($extensions as $priority => $ext_view) {
  76. if (isset($views[$ext_view])) {
  77. $view_list[$priority] = $views[$ext_view][500];
  78. }
  79. }
  80. if (count($view_list) > 0) {
  81. $views[$view] = $view_list;
  82. }
  83. }
  84. ksort($views);
  85. // now overrides
  86. foreach ($views as $view => $view_list) {
  87. if (!empty($overrides[$viewtype][$view])) {
  88. $overrides_list = array();
  89. foreach ($overrides[$viewtype][$view] as $i => $location) {
  90. $component = new ViewComponent();
  91. $component->overridden = true;
  92. $component->view = $view;
  93. $component->location = "{$location}{$viewtype}/";
  94. $overrides_list["o:$i"] = $component;
  95. }
  96. $views[$view] = $overrides_list + $view_list;
  97. }
  98. }
  99. // view handlers
  100. $handlers = _elgg_services()->hooks->getAllHandlers();
  101. $filtered_views = array();
  102. if (!empty($handlers['view'])) {
  103. $filtered_views = array_keys($handlers['view']);
  104. }
  105. $global_hooks = array();
  106. if (!empty($handlers['view']['all'])) {
  107. $global_hooks[] = 'view,all';
  108. }
  109. if (!empty($handlers['display']['view'])) {
  110. $global_hooks[] = 'display,view';
  111. }
  112. if (!empty($handlers['display']['all'])) {
  113. $global_hooks[] = 'display,all';
  114. }
  115. return array(
  116. 'views' => $views,
  117. 'global_hooks' => $global_hooks,
  118. 'filtered_views' => $filtered_views,
  119. );
  120. }
  121. /**
  122. * Get Elgg widget information
  123. *
  124. * @return array [widget] => array(name, contexts)
  125. */
  126. public function getWidgets() {
  127. $tree = array();
  128. foreach (_elgg_services()->widgets->getAllTypes() as $handler => $handler_obj) {
  129. $tree[$handler] = array($handler_obj->name, implode(',', array_values($handler_obj->context)));
  130. }
  131. ksort($tree);
  132. return $tree;
  133. }
  134. /**
  135. * Get Elgg actions information
  136. *
  137. * returns [action] => array(file, access)
  138. *
  139. * @return array
  140. */
  141. public function getActions() {
  142. $tree = array();
  143. $access = array(
  144. 'public' => 'public',
  145. 'logged_in' => 'logged in only',
  146. 'admin' => 'admin only',
  147. );
  148. $start = strlen(elgg_get_root_path());
  149. foreach (_elgg_services()->actions->getAllActions() as $action => $info) {
  150. $info['file'] = substr($info['file'], $start);
  151. $tree[$action] = array($info['file'], $access[$info['access']]);
  152. }
  153. ksort($tree);
  154. return $tree;
  155. }
  156. /**
  157. * Get simplecache information
  158. *
  159. * @return array [views]
  160. */
  161. public function getSimpleCache() {
  162. global $CONFIG;
  163. $tree = array();
  164. foreach ($CONFIG->views->simplecache as $view => $foo) {
  165. $tree[$view] = "";
  166. }
  167. ksort($tree);
  168. return $tree;
  169. }
  170. /**
  171. * Get Elgg web services API methods
  172. *
  173. * @return array [method] => array(function, parameters, call_method, api auth, user auth)
  174. */
  175. public function getWebServices() {
  176. global $API_METHODS;
  177. $tree = array();
  178. foreach ($API_METHODS as $method => $info) {
  179. $params = implode(', ', array_keys($info['parameters']));
  180. if (!$params) {
  181. $params = 'none';
  182. }
  183. $tree[$method] = array(
  184. $info['function'],
  185. "params: $params",
  186. $info['call_method'],
  187. ($info['require_api_auth']) ? 'API authentication required' : 'No API authentication required',
  188. ($info['require_user_auth']) ? 'User authentication required' : 'No user authentication required',
  189. );
  190. }
  191. ksort($tree);
  192. return $tree;
  193. }
  194. /**
  195. * Get information about registered menus
  196. *
  197. * @return array [menu name] => array(item name => array(text, href, section, parent))
  198. */
  199. public function getMenus() {
  200. $menus = elgg_get_config('menus');
  201. // get JIT menu items
  202. // note that 'river' is absent from this list - hooks attempt to get object/subject entities cause problems
  203. $jit_menus = array('annotation', 'entity', 'login', 'longtext', 'owner_block', 'user_hover', 'widget');
  204. // create generic ElggEntity, ElggAnnotation, ElggUser, ElggWidget
  205. $annotation = new \ElggAnnotation();
  206. $annotation->id = 999;
  207. $annotation->name = 'generic_comment';
  208. $annotation->value = 'testvalue';
  209. $entity = new \ElggObject();
  210. $entity->guid = 999;
  211. $entity->subtype = 'blog';
  212. $entity->title = 'test entity';
  213. $entity->access_id = ACCESS_PUBLIC;
  214. $user = new \ElggUser();
  215. $user->guid = 999;
  216. $user->name = "Test User";
  217. $user->username = 'test_user';
  218. $widget = new \ElggWidget();
  219. $widget->guid = 999;
  220. $widget->title = 'test widget';
  221. // call plugin hooks
  222. foreach ($jit_menus as $type) {
  223. $params = array('entity' => $entity, 'annotation' => $annotation, 'user' => $user);
  224. switch ($type){
  225. case 'owner_block':
  226. case 'user_hover':
  227. $params['entity'] = $user;
  228. break;
  229. case 'widget':
  230. // this does not work because you cannot set a guid on an entity
  231. $params['entity'] = $widget;
  232. break;
  233. default:
  234. break;
  235. }
  236. $menus[$type] = elgg_trigger_plugin_hook('register', "menu:$type", $params, array());
  237. }
  238. // put the menus in tree form for inspection
  239. $tree = array();
  240. foreach ($menus as $menu_name => $attributes) {
  241. foreach ($attributes as $item) {
  242. /* @var \ElggMenuItem $item */
  243. $name = $item->getName();
  244. $text = htmlspecialchars($item->getText(), ENT_QUOTES, 'UTF-8', false);
  245. $href = $item->getHref();
  246. if ($href === false) {
  247. $href = 'not a link';
  248. } elseif ($href === "") {
  249. $href = 'not a direct link - possibly ajax';
  250. }
  251. $section = $item->getSection();
  252. $parent = $item->getParentName();
  253. if (!$parent) {
  254. $parent = 'none';
  255. }
  256. $tree[$menu_name][$name] = array(
  257. "text: $text",
  258. "href: $href",
  259. "section: $section",
  260. "parent: $parent",
  261. );
  262. }
  263. }
  264. ksort($tree);
  265. return $tree;
  266. }
  267. /**
  268. * Get a string description of a callback
  269. *
  270. * E.g. "function_name", "Static::method", "(ClassName)->method", "(Closure path/to/file.php:23)"
  271. *
  272. * @param mixed $callable The callable value to describe
  273. * @param string $file_root if provided, it will be removed from the beginning of file names
  274. * @return string
  275. */
  276. public function describeCallable($callable, $file_root = '') {
  277. if (is_string($callable)) {
  278. return $callable;
  279. }
  280. if (is_array($callable) && array_keys($callable) === array(0, 1) && is_string($callable[1])) {
  281. if (is_string($callable[0])) {
  282. return "{$callable[0]}::{$callable[1]}";
  283. }
  284. return "(" . get_class($callable[0]) . ")->{$callable[1]}";
  285. }
  286. if ($callable instanceof \Closure) {
  287. $ref = new \ReflectionFunction($callable);
  288. $file = $ref->getFileName();
  289. $line = $ref->getStartLine();
  290. if ($file_root && 0 === strpos($file, $file_root)) {
  291. $file = substr($file, strlen($file_root));
  292. }
  293. return "(Closure {$file}:{$line})";
  294. }
  295. if (is_object($callable)) {
  296. return "(" . get_class($callable) . ")->__invoke()";
  297. }
  298. return "(unknown)";
  299. }
  300. /**
  301. * Build a tree of event handlers
  302. *
  303. * @param array $all_handlers Set of handlers from a HooksRegistrationService
  304. *
  305. * @return array
  306. */
  307. protected function buildHandlerTree($all_handlers) {
  308. $tree = array();
  309. $root = elgg_get_root_path();
  310. foreach ($all_handlers as $hook => $types) {
  311. foreach ($types as $type => $handlers) {
  312. array_walk($handlers, function (&$callable, $priority) use ($root) {
  313. $description = $this->describeCallable($callable, $root);
  314. $callable = "$priority: $description";
  315. });
  316. $tree[$hook . ',' . $type] = $handlers;
  317. }
  318. }
  319. ksort($tree);
  320. return $tree;
  321. }
  322. /**
  323. * Create array of all php files in directory and subdirectories
  324. *
  325. * @param string $dir full path to directory to begin search
  326. * @return array of every php file in $dir or below in file tree
  327. */
  328. protected function recurseFileTree($dir) {
  329. $view_list = array();
  330. $handle = opendir($dir);
  331. while ($file = readdir($handle)) {
  332. if ($file[0] == '.') {
  333. } else if (is_dir($dir . $file)) {
  334. $view_list = array_merge($view_list, $this->recurseFileTree($dir . $file . "/"));
  335. } else {
  336. $extension = strrchr(trim($file, "/"), '.');
  337. if ($extension === ".php") {
  338. $view_list[] = $dir . $file;
  339. }
  340. }
  341. }
  342. closedir($handle);
  343. return $view_list;
  344. }
  345. }