PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/class.component_registry.php

https://bitbucket.org/photocrati/pope-framework
PHP | 908 lines | 536 code | 157 blank | 215 comment | 114 complexity | e8ded7bf041058a1fa4d1ffa774d8e12 MD5 | raw file
  1. <?php
  2. if (!defined('POPE_VERSION')) { die('Use autoload.php'); }
  3. /**
  4. * A registry of registered products, modules, adapters, and utilities.
  5. *
  6. *
  7. * How the registry gets initialized:
  8. * 1) Each product tells the registry where to find products and modules
  9. * 2) We load all products
  10. */
  11. class C_Component_Registry
  12. {
  13. static $_instance = NULL;
  14. var $_searched_paths = array();
  15. var $_blacklist = array();
  16. var $_meta_info = array();
  17. var $_default_path = NULL;
  18. var $_modules = array();
  19. var $_products = array();
  20. var $_adapters = array();
  21. var $_utilities = array();
  22. var $_module_type_cache = array();
  23. var $_module_type_cache_count = 0;
  24. /**
  25. * This is a singleton object
  26. */
  27. private function __construct()
  28. {
  29. // Create an autoloader
  30. spl_autoload_register(array($this, '_module_autoload'), TRUE);
  31. }
  32. /**
  33. * Returns a singleton
  34. * @return C_Component_Registry()
  35. */
  36. static function &get_instance()
  37. {
  38. if (is_null(self::$_instance)) {
  39. $klass = get_class();
  40. self::$_instance = new $klass();
  41. }
  42. return self::$_instance;
  43. }
  44. function require_module_file($module_file_abspath)
  45. {
  46. // We don't include (require) module files that have the same name. This
  47. // avoids loading module.autoupdate.php from two products
  48. static $already_required = array();
  49. $relpath = basename($module_file_abspath);
  50. if (!in_array($relpath, $already_required)) {
  51. @require_once($module_file_abspath);
  52. $already_required[] = $relpath;
  53. }
  54. }
  55. function has_searched_path_before($abspath)
  56. {
  57. return in_array($abspath, $this->_searched_paths);
  58. }
  59. function mark_as_searched_path($abspath)
  60. {
  61. $this->_searched_paths[] = $abspath;
  62. }
  63. /**
  64. * Adds a path in the search paths for loading modules
  65. * @param string $path
  66. * @param bool $recurse - TRUE, FALSE, or the number of levels to recurse
  67. * @param bool $load_all - loads all modules found in the path
  68. */
  69. function add_module_path($path, $recurse = false, $load_all = false)
  70. {
  71. if (!$recurse || (!$this->has_searched_path_before($path))) {
  72. // If no default module path has been set, then set one now
  73. if ($this->get_default_module_path() == null) {
  74. $this->set_default_module_path($path);
  75. }
  76. // We we've been passed a module file, then include it
  77. if (@file_exists($path) && is_file($path)) {
  78. $this->require_module_file($path);
  79. }
  80. // Recursively find product and module files in this path
  81. else foreach ($this->find_product_and_module_files($path, $recurse) as $file_abspath) {
  82. $this->require_module_file($file_abspath);
  83. }
  84. $this->mark_as_searched_path($path);
  85. }
  86. if ($load_all) $this->load_all_modules(NULL, $path);
  87. }
  88. /**
  89. * Retrieves the default module path (Note: this is just the generic root container path for modules)
  90. * @return string
  91. */
  92. function get_default_module_path()
  93. {
  94. return $this->_default_path;
  95. }
  96. /**
  97. * Sets the default module path (Note: this is just the generic root container path for modules)
  98. * @param string $path
  99. */
  100. function set_default_module_path($path)
  101. {
  102. $this->_default_path = $path;
  103. }
  104. /**
  105. * Retrieves the module path
  106. * @param string $module_id
  107. * @return string
  108. */
  109. function get_module_path($module_id)
  110. {
  111. if (isset($this->_meta_info[$module_id])) {
  112. $info = $this->_meta_info[$module_id];
  113. if (isset($info['path'])) {
  114. return $info['path'];
  115. }
  116. }
  117. return null;
  118. }
  119. /**
  120. * Retrieves the module installation directory
  121. * @param string $module_id
  122. * @return string
  123. */
  124. function get_module_dir($module_id)
  125. {
  126. $path = $this->get_module_path($module_id);
  127. if ($path != null) {
  128. return dirname($path);
  129. }
  130. return null;
  131. }
  132. function is_module_loaded($module_id)
  133. {
  134. return (isset($this->_meta_info[$module_id]) && isset($this->_meta_info[$module_id]['loaded']) && $this->_meta_info[$module_id]['loaded']);
  135. }
  136. /**
  137. * Loads a module's code according to its dependency list
  138. * @param string $module_id
  139. */
  140. function load_module($module_id)
  141. {
  142. $retval = FALSE;
  143. if (($module = $this->get_module($module_id)) && !$this->is_module_loaded($module_id) && !$this->is_blacklisted($module_id)) {
  144. $module->load();
  145. $retval = $this->_meta_info[$module_id]['loaded'] = TRUE;
  146. }
  147. return $retval;
  148. }
  149. function load_all_modules($type=NULL, $dir=NULL)
  150. {
  151. $modules = $this->get_known_module_list();
  152. $ret = true;
  153. foreach ($modules as $module_id)
  154. {
  155. if ($type == null || $this->get_module_meta($module_id, 'type') == $type) {
  156. if ($dir == NULL || strpos($this->get_module_dir($module_id), $dir) !== FALSE)
  157. $ret = $this->load_module($module_id) && $ret;
  158. }
  159. }
  160. return $ret;
  161. }
  162. /**
  163. * Initializes a previously loaded module
  164. * @param string $module_id
  165. */
  166. function initialize_module($module_id)
  167. {
  168. $retval = FALSE;
  169. if (isset($this->_modules[$module_id])) {
  170. $module = $this->_modules[$module_id];
  171. if ($this->is_module_loaded($module_id) && !$module->initialized) {
  172. if (method_exists($module, 'initialize'))
  173. $module->initialize();
  174. $module->initialized = true;
  175. }
  176. $retval = TRUE;
  177. }
  178. return $retval;
  179. }
  180. /**
  181. * Initializes an already loaded product
  182. * @param string $product_id
  183. * @return bool
  184. */
  185. function initialize_product($product_id)
  186. {
  187. return $this->initialize_module($product_id);
  188. }
  189. /**
  190. * Initializes all previously loaded modules
  191. */
  192. function initialize_all_modules()
  193. {
  194. $module_list = $this->get_loaded_module_list();
  195. foreach ($module_list as $module_id)
  196. {
  197. $this->initialize_module($module_id);
  198. }
  199. }
  200. /**
  201. * Adds an already loaded module to the registry
  202. * @param string $module_id
  203. * @param C_Base_Module $module_object
  204. */
  205. function add_module($module_id, $module_object)
  206. {
  207. if (!isset($this->_modules[$module_id])) {
  208. $this->_modules[$module_id] = $module_object;
  209. }
  210. if (!isset($this->_meta_info[$module_id])) {
  211. $klass = new ReflectionClass($module_object);
  212. $this->_meta_info[$module_id] = array(
  213. 'path' => $klass->getFileName(),
  214. 'type' => $klass->isSubclassOf('C_Base_Product') ? 'product' : 'module',
  215. 'loaded' => FALSE
  216. );
  217. }
  218. }
  219. /**
  220. * Deletes an already loaded module from the registry
  221. * @param string $module_id
  222. */
  223. function del_module($module_id)
  224. {
  225. if (isset($this->_modules[$module_id])) {
  226. unset($this->_modules[$module_id]);
  227. }
  228. }
  229. /**
  230. * Retrieves the instance of the registered module. Note: it's the instance of the module object, so the module needs to be loaded or this function won't return anything. For module info returned by scanning (with add_module_path), look at get_module_meta
  231. * @param string $module_id
  232. * @return C_Base_Module
  233. */
  234. function get_module($module_id)
  235. {
  236. if (isset($this->_modules[$module_id])) {
  237. return $this->_modules[$module_id];
  238. }
  239. return null;
  240. }
  241. function get_module_meta($module_id, $meta_name)
  242. {
  243. $meta = $this->get_module_meta_list($module_id);
  244. if (isset($meta[$meta_name])) {
  245. return $meta[$meta_name];
  246. }
  247. return null;
  248. }
  249. function get_module_meta_list($module_id)
  250. {
  251. if (isset($this->_meta_info[$module_id])) {
  252. return $this->_meta_info[$module_id];
  253. }
  254. return null;
  255. }
  256. /**
  257. * Retrieves a list of instantiated module ids, in their "loaded" order as defined by a product
  258. *
  259. * @return array
  260. */
  261. function get_module_list($for_product_id=FALSE)
  262. {
  263. $retval = $module_list = array();
  264. // As of May 1, 2015, there's a new standard. A product will provide get_provided_modules() and get_modules_to_load().
  265. // As of Feb 10, 2015, there's no standard way across Pope products to an "ordered" list of modules
  266. // that the product provides.
  267. //
  268. // The "standard" going forward will insist that all Product classes will provide either:
  269. // A) a static property called "modules"
  270. // B) an instance method called "define_modules", which returns a list of modules, and as well, sets
  271. // a static property called "modules'.
  272. //
  273. // IMPORTANT!
  274. // The Photocrati Theme, as of version 4.1.8, doesn't follow this standard. But both NextGEN Pro and Plus do.
  275. // Following the standard above, collect all modules provided by a product
  276. $problematic_product_id = FALSE;
  277. foreach ($this->get_product_list() as $product_id) {
  278. $modules = array();
  279. // Try getting the list of modules using the "standard" described above
  280. $obj = $this->get_product($product_id);
  281. try{
  282. $klass = new ReflectionClass($obj);
  283. if ($klass->hasMethod('get_modules_to_load')) {
  284. $modules = $obj->get_modules_provided();
  285. }
  286. elseif ($klass->hasProperty('modules')) {
  287. $modules = $klass->getStaticPropertyValue('modules');
  288. }
  289. if (!$modules && $klass->hasMethod('define_modules')) {
  290. $modules = $obj->define_modules();
  291. if ($klass->hasProperty('modules')) {
  292. $modules = $klass->getStaticPropertyValue('modules');
  293. }
  294. }
  295. }
  296. // We've encountered a product that doesn't follow the standard. For these exceptions, we'll have to
  297. // make an educated guess - if the module path is in the product's default module path, we know that
  298. // it belongs to the product
  299. catch (ReflectionException $ex) {
  300. $modules = array();
  301. }
  302. if (!$modules) {
  303. $product_path = $this->get_product_module_path($product_id);
  304. foreach ($this->_modules as $module_id => $module) {
  305. if (strpos($this->get_module_path($module_id), $product_path) !== FALSE) {
  306. $modules[] = $module_id;
  307. }
  308. }
  309. if (!$modules) $problematic_product_id = $product_id;
  310. }
  311. $module_list[$product_id] = $modules;
  312. }
  313. // If we have a problematic product, that is, one that we can't find it's ordered list of modules
  314. // that it provides, then we have one last fallback: get a list of modules that Pope is aware of, but hasn't
  315. // added to $module_list[$product_id] yet
  316. if ($problematic_product_id) {
  317. $modules = array();
  318. foreach (array_keys($this->_modules) as $module_id) {
  319. $assigned = FALSE;
  320. foreach (array_keys($module_list) as $product_id) {
  321. if (in_array($module_id, $module_list[$product_id])) {
  322. $assigned =TRUE;
  323. break;
  324. }
  325. }
  326. if (!$assigned) $modules[] = $module_id;
  327. }
  328. $module_list[$problematic_product_id] = $modules;
  329. }
  330. // Now that we know which products provide which modules, we can serve the request.
  331. if (!$for_product_id) {
  332. foreach (array_values($module_list) as $modules) {
  333. $retval = array_merge($retval, $modules);
  334. }
  335. }
  336. else $retval = $module_list[$for_product_id];
  337. // Final fallback...if all else fails, just return the list of all modules
  338. // that Pope is aware of
  339. if (!$retval) $retval = array_keys($this->_modules);
  340. return $retval;
  341. }
  342. function get_loaded_module_list()
  343. {
  344. $retval = array();
  345. foreach ($this->get_module_list() as $module_id) {
  346. if ($this->is_module_loaded($module_id)) $retval[] = $module_id;
  347. }
  348. return $retval;
  349. }
  350. /**
  351. * Retrieves a list of registered module ids, including those that aren't loaded (i.e. get_module() call with those unloaded ids will fail)
  352. * @return array
  353. */
  354. function get_known_module_list()
  355. {
  356. return array_keys($this->_meta_info);
  357. }
  358. function load_product($product_id)
  359. {
  360. return $this->load_module($product_id);
  361. }
  362. function load_all_products()
  363. {
  364. return $this->load_all_modules('product');
  365. }
  366. /**
  367. * Adds an already loaded product in the registry
  368. * @param string $product_id
  369. * @param C_Base_Module $product_object
  370. */
  371. function add_product($product_id, $product_object)
  372. {
  373. if (!isset($this->_products[$product_id])) {
  374. $this->_products[$product_id] = $product_object;
  375. }
  376. }
  377. /**
  378. * Deletes an already loaded product from the registry
  379. * @param string $product_id
  380. */
  381. function del_product($product_id)
  382. {
  383. if (isset($this->_products[$product_id])) {
  384. unset($this->_products[$product_id]);
  385. }
  386. }
  387. /**
  388. * Retrieves the instance of the registered product
  389. * @param string $product_id
  390. * @return C_Base_Module
  391. */
  392. function get_product($product_id)
  393. {
  394. if (isset($this->_products[$product_id])) {
  395. return $this->_products[$product_id];
  396. }
  397. return null;
  398. }
  399. function get_product_meta($product_id, $meta_name)
  400. {
  401. $meta = $this->get_product_meta_list($product_id);
  402. if (isset($meta[$meta_name])) {
  403. return $meta[$meta_name];
  404. }
  405. return null;
  406. }
  407. function get_product_meta_list($product_id)
  408. {
  409. if (isset($this->_meta_info[$product_id]) && $this->_meta_info[$product_id]['type'] == 'product') {
  410. return $this->_meta_info[$product_id];
  411. }
  412. return null;
  413. }
  414. /**
  415. * Retrieves the module installation path for a specific product (Note: this is just the generic root container path for modules of this product)
  416. * @param string $product_id
  417. * @return string
  418. */
  419. function get_product_module_path($product_id)
  420. {
  421. if (isset($this->_meta_info[$product_id])) {
  422. $info = $this->_meta_info[$product_id];
  423. if (isset($info['product-module-path'])) {
  424. return $info['product-module-path'];
  425. }
  426. }
  427. return null;
  428. }
  429. function blacklist_module_file($relpath)
  430. {
  431. if (!in_array($relpath, $this->_blacklist)) $this->_blacklist[] = $relpath;
  432. }
  433. function is_blacklisted($filename)
  434. {
  435. return in_array($filename, $this->_blacklist);
  436. }
  437. /**
  438. * Sets the module installation path for a specific product (Note: this is just the generic root container path for modules of this product)
  439. * @param string $product_id
  440. * @param string $module_path
  441. */
  442. function set_product_module_path($product_id, $module_path)
  443. {
  444. if (isset($this->_meta_info[$product_id])) {
  445. $this->_meta_info[$product_id]['product-module-path'] = $module_path;
  446. }
  447. }
  448. /**
  449. * Retrieves a list of instantiated product ids
  450. * @return array
  451. */
  452. function get_product_list()
  453. {
  454. return array_keys($this->_products);
  455. }
  456. /**
  457. * Retrieves a list of registered product ids, including those that aren't loaded (i.e. get_product() call with those unloaded ids will fail)
  458. * @return array
  459. */
  460. function get_known_product_list()
  461. {
  462. $list = array_keys($this->_meta_info);
  463. $return = array();
  464. foreach ($list as $module_id)
  465. {
  466. if ($this->get_product_meta_list($module_id) != null)
  467. {
  468. $return[] = $module_id;
  469. }
  470. }
  471. return $return;
  472. }
  473. /**
  474. * Registers an adapter for an interface with specific contexts
  475. * @param string $interface
  476. * @param string $class
  477. * @param array $contexts
  478. */
  479. function add_adapter($interface, $class, $contexts=FALSE)
  480. {
  481. // If no specific contexts are given, then we assume
  482. // that the adapter is to be applied in ALL contexts
  483. if (!$contexts) $contexts = array('all');
  484. if (!is_array($contexts)) $contexts = array($contexts);
  485. if (!isset($this->_adapters[$interface])) {
  486. $this->_adapters[$interface] = array();
  487. }
  488. // Iterate through each specific context
  489. foreach ($contexts as $context) {
  490. if (!isset($this->_adapters[$interface][$context])) {
  491. $this->_adapters[$interface][$context] = array();
  492. }
  493. $this->_adapters[$interface][$context][] = $class;
  494. }
  495. }
  496. /**
  497. * Removes an adapter for an interface. May optionally specifify what
  498. * contexts to remove the adapter from, leaving the rest intact
  499. * @param string $interface
  500. * @param string $class
  501. * @param array $contexts
  502. */
  503. function del_adapter($interface, $class, $contexts=FALSE)
  504. {
  505. // Ensure that contexts is an array of contexts
  506. if (!$contexts) $contexts = array('all');
  507. if (!is_array($contexts)) $contexts = array($contexts);
  508. // Iterate through each context for an adapter
  509. foreach ($this->_adapters[$interface] as $context => $classes) {
  510. if (!$context OR in_array($context, $contexts)) {
  511. $index = array_search($class, $classes);
  512. unset($this->_adapters[$interface][$context][$index]);
  513. }
  514. }
  515. }
  516. /**
  517. * Apply adapters registered for the component
  518. * @param C_Component $component
  519. * @return C_Component
  520. */
  521. function &apply_adapters(C_Component &$component)
  522. {
  523. // Iterate through each adapted interface. If the component implements
  524. // the interface, then apply the adapters
  525. foreach ($this->_adapters as $interface => $contexts) {
  526. if ($component->implements_interface($interface)) {
  527. // Determine what context apply to the current component
  528. $applied_contexts = array('all');
  529. if ($component->context) {
  530. $applied_contexts[] = $component->context;
  531. $applied_contexts = $this->_flatten_array($applied_contexts);
  532. }
  533. // Iterate through each of the components contexts and apply the
  534. // registered adapters
  535. foreach ($applied_contexts as $context) {
  536. if (isset($contexts[$context])) {
  537. foreach ($contexts[$context] as $adapter) {
  538. $component->add_mixin($adapter, FALSE);
  539. }
  540. }
  541. }
  542. }
  543. }
  544. return $component;
  545. }
  546. /**
  547. * Adds a utility for an interface, to be used in particular contexts
  548. * @param string $interface
  549. * @param string $class
  550. * @param array $contexts
  551. */
  552. function add_utility($interface, $class, $contexts=FALSE)
  553. {
  554. // If no specific contexts are given, then we assume
  555. // that the utility is for ALL contexts
  556. if (!$contexts) $contexts = array('all');
  557. if (!is_array($contexts)) $contexts = array($contexts);
  558. if (!isset($this->_utilities[$interface])) {
  559. $this->_utilities[$interface] = array();
  560. }
  561. // Add the utility for each appropriate context
  562. foreach ($contexts as $context) {
  563. $this->_utilities[$interface][$context] = $class;
  564. }
  565. }
  566. /**
  567. * Deletes a registered utility for a particular interface.
  568. * @param string $interface
  569. * @param array $contexts
  570. */
  571. function del_utility($interface, $contexts=FALSE)
  572. {
  573. if (!$contexts) $contexts = array('all');
  574. if (!is_array($contexts)) $contexts = array($contexts);
  575. // Iterate through each context for an interface
  576. foreach ($this->_utilities[$interface] as $context => $class) {
  577. if (!$context OR in_array($context, $contexts)) {
  578. unset($this->_utilities[$interface][$context]);
  579. }
  580. }
  581. }
  582. /**
  583. * Gets the class name of the component providing a utility implementation
  584. * @param string $interface
  585. * @param string|array $context
  586. * @return string
  587. */
  588. function get_utility_class_name($interface, $context=FALSE)
  589. {
  590. return $this->_retrieve_utility_class($interface, $context);
  591. }
  592. /**
  593. * Retrieves an instantiates the registered utility for the provided instance.
  594. * The instance is a singleton and must provide the get_instance() method
  595. * @param string $interface
  596. * @param string $context
  597. * @return C_Component
  598. */
  599. function get_utility($interface, $context=FALSE)
  600. {
  601. if (!$context) $context='all';
  602. $class = $this->_retrieve_utility_class($interface, $context);
  603. return call_user_func("{$class}::get_instance", $context);
  604. }
  605. /**
  606. * Flattens an array of arrays to a single array
  607. * @param array $array
  608. * @param array $parent (optional)
  609. * @param bool $exclude_duplicates (optional - defaults to TRUE)
  610. * @return array
  611. */
  612. function _flatten_array($array, $parent=NULL, $exclude_duplicates=TRUE)
  613. {
  614. if (is_array($array)) {
  615. // We're to add each element to the parent array
  616. if ($parent) {
  617. foreach ($array as $index => $element) {
  618. foreach ($this->_flatten_array($array) as $sub_element) {
  619. if ($exclude_duplicates) {
  620. if (!in_array($sub_element, $parent)) {
  621. $parent[] = $sub_element;
  622. }
  623. }
  624. else $parent[] = $sub_element;
  625. }
  626. }
  627. $array = $parent;
  628. }
  629. // We're starting the process..
  630. else {
  631. $index = 0;
  632. while (isset($array[$index])) {
  633. $element = $array[$index];
  634. if (is_array($element)) {
  635. $array = $this->_flatten_array($element, $array);
  636. unset($array[$index]);
  637. }
  638. $index += 1;
  639. }
  640. $array = array_values($array);
  641. }
  642. }
  643. else {
  644. $array = array($array);
  645. }
  646. return $array;
  647. }
  648. function find_product_and_module_files($abspath, $recursive=FALSE)
  649. {
  650. $retval = array();
  651. static $recursive_level = 0;
  652. $recursive_level++;
  653. $abspath = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $abspath);
  654. $contents = @scandir($abspath);
  655. if ($contents) foreach ($contents as $filename) {
  656. if ($filename == '.' || $filename == '..' || $filename == 'error_log') continue;
  657. $filename_abspath = $abspath.DIRECTORY_SEPARATOR.$filename;
  658. // Is this a subdirectory?
  659. // We don't use is_dir(), as it's less efficient than just checking for a 'dot' in the filename.
  660. // The problem is that we're assuming that our directories won't contain a 'dot'.
  661. if ($recursive && strpos($filename, '.') === FALSE) {
  662. // The recursive parameter can either be set to TRUE or the number of levels to navigate
  663. // If we reach the max number of recursive levels we're supported to navigate, then we try
  664. // to guess if there's a module or product file under the directory with the same name as
  665. // the directory
  666. if ($recursive === TRUE || (is_int($recursive) && $recursive_level <= $recursive)) {
  667. $retval = array_merge($retval, $this->find_product_and_module_files($filename_abspath, $recursive));
  668. }
  669. elseif (@file_exists(($module_abspath = $filename_abspath.DIRECTORY_SEPARATOR.'module.'.$filename.'.php'))) {
  670. $filename = 'module.'.$filename.'.php';
  671. $filename_abspath = $module_abspath;
  672. }
  673. elseif (@file_exists(($product_abspath = $filename_abspath.DIRECTORY_SEPARATOR.'product.'.$filename.'.php'))) {
  674. $filename = 'product.'.$filename.'.php';
  675. $filename_abspath = $module_abspath;
  676. }
  677. }
  678. if ((strpos($filename, 'module.') === 0 OR strpos($filename, 'product.') === 0) AND !$this->is_blacklisted($filename)) {
  679. $retval[] = $filename_abspath;
  680. }
  681. }
  682. $this->mark_as_searched_path($abspath);
  683. $recursive_level--;
  684. return $retval;
  685. }
  686. /**
  687. * Private API method. Retrieves the class which currently provides the utility
  688. * @param string $interface
  689. * @param string $context
  690. */
  691. function _retrieve_utility_class($interface, $context='all')
  692. {
  693. $class = FALSE;
  694. if (!$context) $context = 'all';
  695. if (isset($this->_utilities[$interface])) {
  696. if (isset($this->_utilities[$interface][$context])) {
  697. $class = $this->_utilities[$interface][$context];
  698. }
  699. // No utility defined for the specified interface
  700. else {
  701. if ($context == 'all') $context = 'default';
  702. $class = $this->_retrieve_utility_class($interface, FALSE);
  703. if (!$class)
  704. throw new Exception("No utility registered for `{$interface}` with the `{$context}` context.");
  705. }
  706. }
  707. else throw new Exception("No utilities registered for `{$interface}`");
  708. return $class;
  709. }
  710. /**
  711. * Autoloads any classes, interfaces, or adapters needed by this module
  712. */
  713. function _module_autoload($name)
  714. {
  715. // Pope classes are always prefixed
  716. if (strpos($name, 'C_') !== 0 && strpos($name, 'A_') !== 0 && strpos($name, 'Mixin_') !== 0) {
  717. return;
  718. }
  719. if ($this->_module_type_cache == null || count($this->_modules) > $this->_module_type_cache_count)
  720. {
  721. $this->_module_type_cache_count = count($this->_modules);
  722. $modules = $this->_modules;
  723. $keys = array();
  724. foreach ($modules as $mod => $properties) $keys[$mod] = $properties->module_version;
  725. if (!($this->_module_type_cache = C_Pope_Cache::get($keys, array()))) {
  726. foreach ($modules as $module_id => $module)
  727. {
  728. $dir = $this->get_module_dir($module_id);
  729. $type_list = $module->get_type_list();
  730. foreach ($type_list as $type => $filename)
  731. {
  732. $this->_module_type_cache[strtolower($type)] = $dir . DIRECTORY_SEPARATOR . $filename;
  733. }
  734. }
  735. C_Pope_Cache::set($keys, $this->_module_type_cache);
  736. }
  737. elseif (is_object($this->_module_type_cache)) $this->_module_type_cache = get_object_vars($this->_module_type_cache);
  738. }
  739. $name = strtolower($name);
  740. if (isset($this->_module_type_cache[$name]))
  741. {
  742. $module_filename = $this->_module_type_cache[$name];
  743. if (file_exists($module_filename))
  744. {
  745. require_once($module_filename);
  746. }
  747. }
  748. }
  749. }