/libraries/src/MVC/Factory/MVCFactory.php

https://github.com/joomla/joomla-cms · PHP · 358 lines · 183 code · 45 blank · 130 comment · 14 complexity · 565b41ed04180e1799c59e4faeadd7dd MD5 · raw file

  1. <?php
  2. /**
  3. * Joomla! Content Management System
  4. *
  5. * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
  6. * @license GNU General Public License version 2 or later; see LICENSE
  7. */
  8. namespace Joomla\CMS\MVC\Factory;
  9. use Joomla\CMS\Application\CMSApplicationInterface;
  10. use Joomla\CMS\Cache\CacheControllerFactoryAwareInterface;
  11. use Joomla\CMS\Cache\CacheControllerFactoryAwareTrait;
  12. use Joomla\CMS\Factory;
  13. use Joomla\CMS\Form\FormFactoryAwareInterface;
  14. use Joomla\CMS\Form\FormFactoryAwareTrait;
  15. use Joomla\CMS\MVC\Model\ModelInterface;
  16. use Joomla\CMS\Router\SiteRouterAwareInterface;
  17. use Joomla\CMS\Router\SiteRouterAwareTrait;
  18. use Joomla\Database\DatabaseAwareInterface;
  19. use Joomla\Database\DatabaseAwareTrait;
  20. use Joomla\Database\DatabaseInterface;
  21. use Joomla\Database\Exception\DatabaseNotFoundException;
  22. use Joomla\Event\DispatcherAwareInterface;
  23. use Joomla\Event\DispatcherAwareTrait;
  24. use Joomla\Input\Input;
  25. /**
  26. * Factory to create MVC objects based on a namespace.
  27. *
  28. * @since 3.10.0
  29. */
  30. class MVCFactory implements MVCFactoryInterface, FormFactoryAwareInterface, SiteRouterAwareInterface
  31. {
  32. use FormFactoryAwareTrait;
  33. use DispatcherAwareTrait;
  34. use DatabaseAwareTrait;
  35. use SiteRouterAwareTrait;
  36. use CacheControllerFactoryAwareTrait;
  37. /**
  38. * The namespace to create the objects from.
  39. *
  40. * @var string
  41. * @since 4.0.0
  42. */
  43. private $namespace;
  44. /**
  45. * The namespace must be like:
  46. * Joomla\Component\Content
  47. *
  48. * @param string $namespace The namespace
  49. *
  50. * @since 4.0.0
  51. */
  52. public function __construct($namespace)
  53. {
  54. $this->namespace = $namespace;
  55. }
  56. /**
  57. * Method to load and return a controller object.
  58. *
  59. * @param string $name The name of the controller
  60. * @param string $prefix The controller prefix
  61. * @param array $config The configuration array for the controller
  62. * @param CMSApplicationInterface $app The app
  63. * @param Input $input The input
  64. *
  65. * @return \Joomla\CMS\MVC\Controller\ControllerInterface
  66. *
  67. * @since 3.10.0
  68. * @throws \Exception
  69. */
  70. public function createController($name, $prefix, array $config, CMSApplicationInterface $app, Input $input)
  71. {
  72. // Clean the parameters
  73. $name = preg_replace('/[^A-Z0-9_]/i', '', $name);
  74. $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix);
  75. $className = $this->getClassName('Controller\\' . ucfirst($name) . 'Controller', $prefix);
  76. if (!$className) {
  77. return null;
  78. }
  79. $controller = new $className($config, $this, $app, $input);
  80. $this->setFormFactoryOnObject($controller);
  81. $this->setDispatcherOnObject($controller);
  82. $this->setRouterOnObject($controller);
  83. $this->setCacheControllerOnObject($controller);
  84. return $controller;
  85. }
  86. /**
  87. * Method to load and return a model object.
  88. *
  89. * @param string $name The name of the model.
  90. * @param string $prefix Optional model prefix.
  91. * @param array $config Optional configuration array for the model.
  92. *
  93. * @return ModelInterface The model object
  94. *
  95. * @since 3.10.0
  96. * @throws \Exception
  97. */
  98. public function createModel($name, $prefix = '', array $config = [])
  99. {
  100. // Clean the parameters
  101. $name = preg_replace('/[^A-Z0-9_]/i', '', $name);
  102. $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix);
  103. if (!$prefix) {
  104. @trigger_error(
  105. sprintf(
  106. 'Calling %s() without a prefix is deprecated.',
  107. __METHOD__
  108. ),
  109. E_USER_DEPRECATED
  110. );
  111. $prefix = Factory::getApplication()->getName();
  112. }
  113. $className = $this->getClassName('Model\\' . ucfirst($name) . 'Model', $prefix);
  114. if (!$className) {
  115. return null;
  116. }
  117. $model = new $className($config, $this);
  118. $this->setFormFactoryOnObject($model);
  119. $this->setDispatcherOnObject($model);
  120. $this->setRouterOnObject($model);
  121. $this->setCacheControllerOnObject($model);
  122. if ($model instanceof DatabaseAwareInterface) {
  123. try {
  124. $model->setDatabase($this->getDatabase());
  125. } catch (DatabaseNotFoundException $e) {
  126. @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED);
  127. $model->setDatabase(Factory::getContainer()->get(DatabaseInterface::class));
  128. }
  129. }
  130. return $model;
  131. }
  132. /**
  133. * Method to load and return a view object.
  134. *
  135. * @param string $name The name of the view.
  136. * @param string $prefix Optional view prefix.
  137. * @param string $type Optional type of view.
  138. * @param array $config Optional configuration array for the view.
  139. *
  140. * @return \Joomla\CMS\MVC\View\ViewInterface The view object
  141. *
  142. * @since 3.10.0
  143. * @throws \Exception
  144. */
  145. public function createView($name, $prefix = '', $type = '', array $config = [])
  146. {
  147. // Clean the parameters
  148. $name = preg_replace('/[^A-Z0-9_]/i', '', $name);
  149. $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix);
  150. $type = preg_replace('/[^A-Z0-9_]/i', '', $type);
  151. if (!$prefix) {
  152. @trigger_error(
  153. sprintf(
  154. 'Calling %s() without a prefix is deprecated.',
  155. __METHOD__
  156. ),
  157. E_USER_DEPRECATED
  158. );
  159. $prefix = Factory::getApplication()->getName();
  160. }
  161. $className = $this->getClassName('View\\' . ucfirst($name) . '\\' . ucfirst($type) . 'View', $prefix);
  162. if (!$className) {
  163. return null;
  164. }
  165. $view = new $className($config);
  166. $this->setFormFactoryOnObject($view);
  167. $this->setDispatcherOnObject($view);
  168. $this->setRouterOnObject($view);
  169. $this->setCacheControllerOnObject($view);
  170. return $view;
  171. }
  172. /**
  173. * Method to load and return a table object.
  174. *
  175. * @param string $name The name of the table.
  176. * @param string $prefix Optional table prefix.
  177. * @param array $config Optional configuration array for the table.
  178. *
  179. * @return \Joomla\CMS\Table\Table The table object
  180. *
  181. * @since 3.10.0
  182. * @throws \Exception
  183. */
  184. public function createTable($name, $prefix = '', array $config = [])
  185. {
  186. // Clean the parameters
  187. $name = preg_replace('/[^A-Z0-9_]/i', '', $name);
  188. $prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix);
  189. if (!$prefix) {
  190. @trigger_error(
  191. sprintf(
  192. 'Calling %s() without a prefix is deprecated.',
  193. __METHOD__
  194. ),
  195. E_USER_DEPRECATED
  196. );
  197. $prefix = Factory::getApplication()->getName();
  198. }
  199. $className = $this->getClassName('Table\\' . ucfirst($name) . 'Table', $prefix)
  200. ?: $this->getClassName('Table\\' . ucfirst($name) . 'Table', 'Administrator');
  201. if (!$className) {
  202. return null;
  203. }
  204. try {
  205. $db = \array_key_exists('dbo', $config) ? $config['dbo'] : $this->getDatabase();
  206. } catch (DatabaseNotFoundException $e) {
  207. @trigger_error(sprintf('Database must be set, this will not be caught anymore in 5.0.'), E_USER_DEPRECATED);
  208. $db = Factory::getContainer()->get(DatabaseInterface::class);
  209. }
  210. return new $className($db);
  211. }
  212. /**
  213. * Returns a standard classname, if the class doesn't exist null is returned.
  214. *
  215. * @param string $suffix The suffix
  216. * @param string $prefix The prefix
  217. *
  218. * @return string|null The class name
  219. *
  220. * @since 3.10.0
  221. */
  222. protected function getClassName(string $suffix, string $prefix)
  223. {
  224. if (!$prefix) {
  225. $prefix = Factory::getApplication();
  226. }
  227. $className = trim($this->namespace, '\\') . '\\' . ucfirst($prefix) . '\\' . $suffix;
  228. if (!class_exists($className)) {
  229. return null;
  230. }
  231. return $className;
  232. }
  233. /**
  234. * Sets the internal form factory on the given object.
  235. *
  236. * @param object $object The object
  237. *
  238. * @return void
  239. *
  240. * @since 4.0.0
  241. */
  242. private function setFormFactoryOnObject($object)
  243. {
  244. if (!$object instanceof FormFactoryAwareInterface) {
  245. return;
  246. }
  247. try {
  248. $object->setFormFactory($this->getFormFactory());
  249. } catch (\UnexpectedValueException $e) {
  250. // Ignore it
  251. }
  252. }
  253. /**
  254. * Sets the internal event dispatcher on the given object.
  255. *
  256. * @param object $object The object
  257. *
  258. * @return void
  259. *
  260. * @since 4.2.0
  261. */
  262. private function setDispatcherOnObject($object)
  263. {
  264. if (!$object instanceof DispatcherAwareInterface) {
  265. return;
  266. }
  267. try {
  268. $object->setDispatcher($this->getDispatcher());
  269. } catch (\UnexpectedValueException $e) {
  270. // Ignore it
  271. }
  272. }
  273. /**
  274. * Sets the internal router on the given object.
  275. *
  276. * @param object $object The object
  277. *
  278. * @return void
  279. *
  280. * @since 4.2.0
  281. */
  282. private function setRouterOnObject($object): void
  283. {
  284. if (!$object instanceof SiteRouterAwareInterface) {
  285. return;
  286. }
  287. try {
  288. $object->setSiteRouter($this->getSiteRouter());
  289. } catch (\UnexpectedValueException $e) {
  290. // Ignore it
  291. }
  292. }
  293. /**
  294. * Sets the internal cache controller on the given object.
  295. *
  296. * @param object $object The object
  297. *
  298. * @return void
  299. *
  300. * @since 4.2.0
  301. */
  302. private function setCacheControllerOnObject($object): void
  303. {
  304. if (!$object instanceof CacheControllerFactoryAwareInterface) {
  305. return;
  306. }
  307. try {
  308. $object->setCacheControllerFactory($this->getCacheControllerFactory());
  309. } catch (\UnexpectedValueException $e) {
  310. // Ignore it
  311. }
  312. }
  313. }