diff --git a/installation/application/web.php b/installation/application/web.php index 4b723d73484bc..114cd77b0da97 100644 --- a/installation/application/web.php +++ b/installation/application/web.php @@ -174,6 +174,9 @@ public function dispatch() // Register the document object with JFactory. JFactory::$document = $document; + // Register our JHtml service + JHtml::getServiceRegistry()->register('installation', new InstallationHtmlHelper($this)); + // Define component path. define('JPATH_COMPONENT', JPATH_BASE); define('JPATH_COMPONENT_SITE', JPATH_SITE); diff --git a/installation/html/helper.php b/installation/html/helper.php index 64cee587d9b58..8bcc74c407070 100644 --- a/installation/html/helper.php +++ b/installation/html/helper.php @@ -15,6 +15,26 @@ */ class InstallationHtmlHelper { + /** + * The active application + * + * @var InstallationApplicationWeb + * @since __DEPLOY_VERSION__ + */ + private $application; + + /** + * Service constructor + * + * @param InstallationApplicationWeb $application The active application + * + * @since __DEPLOY_VERSION__ + */ + public function __construct(InstallationApplicationWeb $application) + { + $this->application = $application; + } + /** * Method to generate the side bar. * @@ -22,7 +42,7 @@ class InstallationHtmlHelper * * @since 1.6 */ - public static function stepbar() + public function stepbar() { // Determine if the configuration file path is writable. $path = JPATH_CONFIGURATION . '/configuration.php'; @@ -44,7 +64,7 @@ public static function stepbar() foreach ($tabs as $tab) { - $html[] = static::getTab($tab, $tabs); + $html[] = $this->getTab($tab, $tabs); } $html[] = ''; @@ -59,7 +79,7 @@ public static function stepbar() * * @since 3.1 */ - public static function stepbarlanguages() + public function stepbarlanguages() { $tabs = array(); $tabs[] = 'languages'; @@ -71,7 +91,7 @@ public static function stepbarlanguages() foreach ($tabs as $tab) { - $html[] = static::getTab($tab, $tabs); + $html[] = $this->getTab($tab, $tabs); } $html[] = ''; @@ -89,11 +109,11 @@ public static function stepbarlanguages() * * @since 3.1 */ - private static function getTab($id, $tabs) + private function getTab($id, $tabs) { - $input = JFactory::getApplication()->input; - $num = static::getTabNumber($id, $tabs); - $view = static::getTabNumber($input->getWord('view'), $tabs); + $input = $this->application->input; + $num = $this->getTabNumber($id, $tabs); + $view = $this->getTabNumber($input->getWord('view'), $tabs); $tab = '' . $num . ' ' . JText::_('INSTL_STEP_' . strtoupper($id) . '_LABEL'); $active = $num == $view ? ' active' : ''; @@ -123,7 +143,7 @@ private static function getTab($id, $tabs) * * @since 3.1 */ - private static function getTabNumber($id, $tabs) + private function getTabNumber($id, $tabs) { $num = (int) array_search($id, $tabs, true); $num++; diff --git a/installation/view/database/tmpl/default.php b/installation/view/database/tmpl/default.php index db1eea760fb14..ff6274fe89c1b 100644 --- a/installation/view/database/tmpl/default.php +++ b/installation/view/database/tmpl/default.php @@ -11,7 +11,7 @@ /* @var InstallationViewDefault $this */ ?> - +
diff --git a/installation/view/defaultlanguage/tmpl/default.php b/installation/view/defaultlanguage/tmpl/default.php index 4cde5517ccc8e..64bfcb8a7713b 100644 --- a/installation/view/defaultlanguage/tmpl/default.php +++ b/installation/view/defaultlanguage/tmpl/default.php @@ -21,7 +21,7 @@ JS ); ?> - +
diff --git a/installation/view/ftp/tmpl/default.php b/installation/view/ftp/tmpl/default.php index 130479a13b0df..d2a08815130e1 100644 --- a/installation/view/ftp/tmpl/default.php +++ b/installation/view/ftp/tmpl/default.php @@ -10,7 +10,7 @@ /* @var InstallationViewDefault $this */ ?> - +
diff --git a/installation/view/languages/tmpl/default.php b/installation/view/languages/tmpl/default.php index d50fc09dff2cf..1444d6f78e731 100644 --- a/installation/view/languages/tmpl/default.php +++ b/installation/view/languages/tmpl/default.php @@ -23,7 +23,7 @@ function installLanguages() { } - +
diff --git a/installation/view/site/tmpl/default.php b/installation/view/site/tmpl/default.php index 59f5a8b7765e0..62533292f9571 100644 --- a/installation/view/site/tmpl/default.php +++ b/installation/view/site/tmpl/default.php @@ -11,7 +11,7 @@ /* @var InstallationViewDefault $this */ ?> - +
diff --git a/installation/view/summary/tmpl/default.php b/installation/view/summary/tmpl/default.php index 6ea0f6045aa8e..a55e7ec6e063e 100644 --- a/installation/view/summary/tmpl/default.php +++ b/installation/view/summary/tmpl/default.php @@ -16,7 +16,7 @@ $useftp = file_exists($path) ? !is_writable($path) : !is_writable(JPATH_CONFIGURATION . '/'); $prev = $useftp ? 'ftp' : 'database'; ?> - +
diff --git a/libraries/cms/html/html.php b/libraries/cms/html/html.php index 0a02fe4b561f1..9b6e2974ed87a 100644 --- a/libraries/cms/html/html.php +++ b/libraries/cms/html/html.php @@ -9,6 +9,8 @@ defined('JPATH_PLATFORM') or die; +use Joomla\CMS\HTML\Registry; +use Joomla\CMS\Log\Log; use Joomla\Utilities\ArrayHelper; jimport('joomla.environment.browser'); @@ -40,6 +42,7 @@ abstract class JHtml * * @var string[] * @since 1.5 + * @deprecated 5.0 */ protected static $includePaths = array(); @@ -48,9 +51,18 @@ abstract class JHtml * * @var callable[] * @since 1.6 + * @deprecated 5.0 */ protected static $registry = array(); + /** + * The service registry for custom and overridden JHtml helpers + * + * @var Registry + * @since __DEPLOY_VERSION__ + */ + protected static $serviceRegistry; + /** * Method to extract a key * @@ -68,6 +80,22 @@ protected static function extract($key) // Check to see whether we need to load a helper file $parts = explode('.', $key); + if (count($parts) === 3) + { + try + { + Log::add( + 'Support for a three segment service key is deprecated and will be removed in Joomla 5.0, use the service registry instead', + Log::WARNING, + 'deprecated' + ); + } + catch (RuntimeException $exception) + { + // Informational message only, continue on + } + } + $prefix = count($parts) === 3 ? array_shift($parts) : 'JHtml'; $file = count($parts) === 2 ? array_shift($parts) : ''; $func = array_shift($parts); @@ -105,6 +133,30 @@ public static function _($key) return static::call($function, $args); } + /* + * Support fetching services from the registry if a custom class prefix was not given (a three segment key), + * the service comes from a class other than this one, and a service has been registered for the file. + */ + if ($prefix === 'JHtml' && $file !== '' && static::getServiceRegistry()->hasService($file)) + { + $service = static::getServiceRegistry()->getService($file); + + $toCall = array($service, $func); + + if (!is_callable($toCall)) + { + throw new InvalidArgumentException(sprintf('%s::%s not found.', $service, $func), 500); + } + + static::register($key, $toCall); + $args = func_get_args(); + + // Remove function name from arguments + array_shift($args); + + return static::call($toCall, $args); + } + $className = $prefix . ucfirst($file); if (!class_exists($className)) @@ -152,6 +204,19 @@ public static function _($key) */ public static function register($key, callable $function) { + try + { + Log::add( + 'Support for registering functions is deprecated and will be removed in Joomla 5.0, use the service registry instead', + Log::WARNING, + 'deprecated' + ); + } + catch (RuntimeException $exception) + { + // Informational message only, continue on + } + list($key) = static::extract($key); static::$registry[$key] = $function; @@ -170,6 +235,19 @@ public static function register($key, callable $function) */ public static function unregister($key) { + try + { + Log::add( + 'Support for registering functions is deprecated and will be removed in Joomla 5.0, use the service registry instead', + Log::WARNING, + 'deprecated' + ); + } + catch (RuntimeException $exception) + { + // Informational message only, continue on + } + list($key) = static::extract($key); if (isset(static::$registry[$key])) @@ -198,6 +276,22 @@ public static function isRegistered($key) return isset(static::$registry[$key]); } + /** + * Retrieves the service registry. + * + * @return Registry + * + * @since __DEPLOY_VERSION__ + */ + public static function getServiceRegistry() + { + if (!static::$serviceRegistry) + { + static::$serviceRegistry = new Registry; + } + + return static::$serviceRegistry; + } /** * Function caller method * @@ -1145,6 +1239,19 @@ public static function calendar($value, $name, $id, $format = '%Y-%m-%d', $attri */ public static function addIncludePath($path = '') { + try + { + Log::add( + 'Support for registering lookup paths is deprecated and will be removed in Joomla 5.0, use the service registry instead', + Log::WARNING, + 'deprecated' + ); + } + catch (RuntimeException $exception) + { + // Informational message only, continue on + } + // Loop through the path directories foreach ((array) $path as $dir) { diff --git a/libraries/src/CMS/HTML/Registry.php b/libraries/src/CMS/HTML/Registry.php new file mode 100644 index 0000000000000..2398d5e005bbd --- /dev/null +++ b/libraries/src/CMS/HTML/Registry.php @@ -0,0 +1,142 @@ + \JHtmlAccess::class, + 'actionsdropdown' => \JHtmlActionsDropdown::class, + 'batch' => \JHtmlBatch::class, + 'behavior' => \JHtmlBehavior::class, + 'bootstrap' => \JHtmlBootstrap::class, + 'category' => \JHtmlCategory::class, + 'content' => \JHtmlContent::class, + 'contentlanguage' => \JHtmlContentlanguage::class, + 'date' => \JHtmlDate::class, + 'debug' => \JHtmlDebug::class, + 'draggablelist' => \JHtmlDraggablelist::class, + 'dropdown' => \JHtmlDropdown::class, + 'email' => \JHtmlEmail::class, + 'form' => \JHtmlForm::class, + 'formbehavior' => \JHtmlFormbehavior::class, + 'grid' => \JHtmlGrid::class, + 'icons' => \JHtmlIcons::class, + 'jgrid' => \JHtmlJGrid::class, + 'jquery' => \JHtmlJquery::class, + 'links' => \JHtmlLinks::class, + 'list' => \JHtmlList::class, + 'menu' => \JHtmlMenu::class, + 'number' => \JHtmlNumber::class, + 'searchtools' => \JHtmlSearchtools::class, + 'select' => \JHtmlSelect::class, + 'sidebar' => \JHtmlSidebar::class, + 'sortablelist' => \JHtmlSortablelist::class, + 'string' => \JHtmlString::class, + 'tag' => \JHtmlTag::class, + 'tel' => \JHtmlTel::class, + 'user' => \JHtmlUser::class, + ]; + + /** + * Array holding the registered services + * + * @var array + * @since __DEPLOY_VERSION__ + */ + private $serviceMap = []; + + /** + * Get the service for a given key + * + * @param string $key The service key to look up + * + * @return string|object + * + * @since __DEPLOY_VERSION__ + */ + public function getService($key) + { + if (!$this->hasService($key)) + { + throw new \InvalidArgumentException("The '$key' service key is not registered."); + } + + return $this->serviceMap[$key]; + } + + /** + * Check if the registry has a service for the given key + * + * @param string $key The service key to look up + * + * @return boolean + * + * @since __DEPLOY_VERSION__ + */ + public function hasService($key) + { + return isset($this->serviceMap[$key]); + } + + /** + * Register a service + * + * @param string $key The service key to be registered + * @param string|object $handler The handler for the service as either a PHP class name or class object + * @param boolean $replace Flag indicating the service key may replace an existing definition + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function register($key, $handler, $replace = false) + { + // If the key exists already and we aren't instructed to replace existing services, bail early + if (isset($this->serviceMap[$key]) && !$replace) + { + throw new \RuntimeException("The '$key' service key is already registered."); + } + + // If the handler is a string, it must be a class that exists + if (is_string($handler) && !class_exists($handler)) + { + throw new \RuntimeException("The '$handler' class for service key '$key' does not exist."); + } + + // Otherwise the handler must be a class object + if (!is_string($handler) && !is_object($handler)) + { + throw new \RuntimeException( + sprintf( + 'The handler for service key %1$s must be a PHP class name or class object, a %2$s was given.', + $key, + gettype($handler) + ) + ); + } + + $this->serviceMap[$key] = $handler; + } +}