[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Layout/ -> FileLayout.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2012 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Layout;
  11  
  12  use Joomla\CMS\Application\ApplicationHelper;
  13  use Joomla\CMS\Component\ComponentHelper;
  14  use Joomla\CMS\Factory;
  15  use Joomla\CMS\Filesystem\Path;
  16  use Joomla\CMS\Version;
  17  
  18  // phpcs:disable PSR1.Files.SideEffects
  19  \defined('JPATH_PLATFORM') or die;
  20  // phpcs:enable PSR1.Files.SideEffects
  21  
  22  /**
  23   * Base class for rendering a display layout
  24   * loaded from from a layout file
  25   *
  26   * @link   https://docs.joomla.org/Special:MyLanguage/Sharing_layouts_across_views_or_extensions_with_JLayout
  27   * @since  3.0
  28   */
  29  class FileLayout extends BaseLayout
  30  {
  31      /**
  32       * Cached layout paths
  33       *
  34       * @var    array
  35       * @since  3.5
  36       */
  37      protected static $cache = array();
  38  
  39      /**
  40       * Dot separated path to the layout file, relative to base path
  41       *
  42       * @var    string
  43       * @since  3.0
  44       */
  45      protected $layoutId = '';
  46  
  47      /**
  48       * Base path to use when loading layout files
  49       *
  50       * @var    string
  51       * @since  3.0
  52       */
  53      protected $basePath = null;
  54  
  55      /**
  56       * Full path to actual layout files, after possible template override check
  57       *
  58       * @var    string
  59       * @since  3.0.3
  60       */
  61      protected $fullPath = null;
  62  
  63      /**
  64       * Paths to search for layouts
  65       *
  66       * @var    array
  67       * @since  3.2
  68       */
  69      protected $includePaths = array();
  70  
  71      /**
  72       * Method to instantiate the file-based layout.
  73       *
  74       * @param   string  $layoutId  Dot separated path to the layout file, relative to base path
  75       * @param   string  $basePath  Base path to use when loading layout files
  76       * @param   mixed   $options   Optional custom options to load. Registry or array format [@since 3.2]
  77       *
  78       * @since   3.0
  79       */
  80      public function __construct($layoutId, $basePath = null, $options = null)
  81      {
  82          // Initialise / Load options
  83          $this->setOptions($options);
  84  
  85          // Main properties
  86          $this->setLayoutId($layoutId);
  87          $this->basePath = $basePath;
  88  
  89          // Init Environment
  90          $this->setComponent($this->options->get('component', 'auto'));
  91          $this->setClient($this->options->get('client', 'auto'));
  92      }
  93  
  94      /**
  95       * Method to render the layout.
  96       *
  97       * @param   array  $displayData  Array of properties available for use inside the layout file to build the displayed output
  98       *
  99       * @return  string  The necessary HTML to display the layout
 100       *
 101       * @since   3.0
 102       */
 103      public function render($displayData = array())
 104      {
 105          $this->clearDebugMessages();
 106  
 107          // Inherit base output from parent class
 108          $layoutOutput = '';
 109  
 110          // Automatically merge any previously data set if $displayData is an array
 111          if (\is_array($displayData)) {
 112              $displayData = array_merge($this->data, $displayData);
 113          }
 114  
 115          // Check possible overrides, and build the full path to layout file
 116          $path = $this->getPath();
 117  
 118          if ($this->isDebugEnabled()) {
 119              echo '<pre>' . $this->renderDebugMessages() . '</pre>';
 120          }
 121  
 122          // Nothing to show
 123          if (empty($path)) {
 124              return $layoutOutput;
 125          }
 126  
 127          ob_start();
 128          include $path;
 129          $layoutOutput .= ob_get_contents();
 130          ob_end_clean();
 131  
 132          return $layoutOutput;
 133      }
 134  
 135      /**
 136       * Method to finds the full real file path, checking possible overrides
 137       *
 138       * @return  string  The full path to the layout file
 139       *
 140       * @since   3.0
 141       */
 142      protected function getPath()
 143      {
 144          $layoutId     = $this->getLayoutId();
 145          $includePaths = $this->getIncludePaths();
 146          $suffixes     = $this->getSuffixes();
 147  
 148          $this->addDebugMessage('<strong>Layout:</strong> ' . $this->layoutId);
 149  
 150          if (!$layoutId) {
 151              $this->addDebugMessage('<strong>There is no active layout</strong>');
 152  
 153              return;
 154          }
 155  
 156          if (!$includePaths) {
 157              $this->addDebugMessage('<strong>There are no folders to search for layouts:</strong> ' . $layoutId);
 158  
 159              return;
 160          }
 161  
 162          $hash = md5(
 163              json_encode(
 164                  array(
 165                      'paths'    => $includePaths,
 166                      'suffixes' => $suffixes,
 167                  )
 168              )
 169          );
 170  
 171          if (!empty(static::$cache[$layoutId][$hash])) {
 172              $this->addDebugMessage('<strong>Cached path:</strong> ' . static::$cache[$layoutId][$hash]);
 173  
 174              return static::$cache[$layoutId][$hash];
 175          }
 176  
 177          $this->addDebugMessage('<strong>Include Paths:</strong> ' . print_r($includePaths, true));
 178  
 179          // Search for suffixed versions. Example: tags.j31.php
 180          if ($suffixes) {
 181              $this->addDebugMessage('<strong>Suffixes:</strong> ' . print_r($suffixes, true));
 182  
 183              foreach ($suffixes as $suffix) {
 184                  $rawPath  = str_replace('.', '/', $this->layoutId) . '.' . $suffix . '.php';
 185                  $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath);
 186  
 187                  if ($foundLayout = Path::find($this->includePaths, $rawPath)) {
 188                      $this->addDebugMessage('<strong>Found layout:</strong> ' . $this->fullPath);
 189  
 190                      static::$cache[$layoutId][$hash] = $foundLayout;
 191  
 192                      return static::$cache[$layoutId][$hash];
 193                  }
 194              }
 195          }
 196  
 197          // Standard version
 198          $rawPath  = str_replace('.', '/', $this->layoutId) . '.php';
 199          $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath);
 200  
 201          $foundLayout = Path::find($this->includePaths, $rawPath);
 202  
 203          if (!$foundLayout) {
 204              $this->addDebugMessage('<strong>Unable to find layout: </strong> ' . $layoutId);
 205  
 206              return;
 207          }
 208  
 209          $this->addDebugMessage('<strong>Found layout:</strong> ' . $foundLayout);
 210  
 211          static::$cache[$layoutId][$hash] = $foundLayout;
 212  
 213          return static::$cache[$layoutId][$hash];
 214      }
 215  
 216      /**
 217       * Add one path to include in layout search. Proxy of addIncludePaths()
 218       *
 219       * @param   string|string[]  $path  The path to search for layouts
 220       *
 221       * @return  self
 222       *
 223       * @since   3.2
 224       */
 225      public function addIncludePath($path)
 226      {
 227          $this->addIncludePaths($path);
 228  
 229          return $this;
 230      }
 231  
 232      /**
 233       * Add one or more paths to include in layout search
 234       *
 235       * @param   string|string[]  $paths  The path or array of paths to search for layouts
 236       *
 237       * @return  self
 238       *
 239       * @since   3.2
 240       */
 241      public function addIncludePaths($paths)
 242      {
 243          if (empty($paths)) {
 244              return $this;
 245          }
 246  
 247          $includePaths = $this->getIncludePaths();
 248  
 249          if (\is_array($paths)) {
 250              $includePaths = array_unique(array_merge($paths, $includePaths));
 251          } else {
 252              array_unshift($includePaths, $paths);
 253          }
 254  
 255          $this->setIncludePaths($includePaths);
 256  
 257          return $this;
 258      }
 259  
 260      /**
 261       * Clear the include paths
 262       *
 263       * @return  self
 264       *
 265       * @since   3.5
 266       */
 267      public function clearIncludePaths()
 268      {
 269          $this->includePaths = array();
 270  
 271          return $this;
 272      }
 273  
 274      /**
 275       * Get the active include paths
 276       *
 277       * @return  array
 278       *
 279       * @since   3.5
 280       */
 281      public function getIncludePaths()
 282      {
 283          if (empty($this->includePaths)) {
 284              $this->includePaths = $this->getDefaultIncludePaths();
 285          }
 286  
 287          return $this->includePaths;
 288      }
 289  
 290      /**
 291       * Get the active layout id
 292       *
 293       * @return  string
 294       *
 295       * @since   3.5
 296       */
 297      public function getLayoutId()
 298      {
 299          return $this->layoutId;
 300      }
 301  
 302      /**
 303       * Get the active suffixes
 304       *
 305       * @return  array
 306       *
 307       * @since   3.5
 308       */
 309      public function getSuffixes()
 310      {
 311          return $this->getOptions()->get('suffixes', array());
 312      }
 313  
 314      /**
 315       * Load the automatically generated language suffixes.
 316       * Example: array('es-ES', 'es', 'ltr')
 317       *
 318       * @return  self
 319       *
 320       * @since   3.5
 321       */
 322      public function loadLanguageSuffixes()
 323      {
 324          $lang = Factory::getLanguage();
 325  
 326          $langTag = $lang->getTag();
 327          $langParts = explode('-', $langTag);
 328  
 329          $suffixes = array($langTag, $langParts[0]);
 330          $suffixes[] = $lang->isRtl() ? 'rtl' : 'ltr';
 331  
 332          $this->setSuffixes($suffixes);
 333  
 334          return $this;
 335      }
 336  
 337      /**
 338       * Load the automatically generated version suffixes.
 339       * Example: array('j311', 'j31', 'j3')
 340       *
 341       * @return  self
 342       *
 343       * @since   3.5
 344       */
 345      public function loadVersionSuffixes()
 346      {
 347          $cmsVersion = new Version();
 348  
 349          // Example j311
 350          $fullVersion = 'j' . str_replace('.', '', $cmsVersion->getShortVersion());
 351  
 352          // Create suffixes like array('j311', 'j31', 'j3')
 353          $suffixes = array(
 354              $fullVersion,
 355              substr($fullVersion, 0, 3),
 356              substr($fullVersion, 0, 2),
 357          );
 358  
 359          $this->setSuffixes(array_unique($suffixes));
 360  
 361          return $this;
 362      }
 363  
 364      /**
 365       * Remove one path from the layout search
 366       *
 367       * @param   string  $path  The path to remove from the layout search
 368       *
 369       * @return  self
 370       *
 371       * @since   3.2
 372       */
 373      public function removeIncludePath($path)
 374      {
 375          $this->removeIncludePaths($path);
 376  
 377          return $this;
 378      }
 379  
 380      /**
 381       * Remove one or more paths to exclude in layout search
 382       *
 383       * @param   string  $paths  The path or array of paths to remove for the layout search
 384       *
 385       * @return  self
 386       *
 387       * @since   3.2
 388       */
 389      public function removeIncludePaths($paths)
 390      {
 391          if (!empty($paths)) {
 392              $paths = (array) $paths;
 393  
 394              $this->includePaths = array_diff($this->includePaths, $paths);
 395          }
 396  
 397          return $this;
 398      }
 399  
 400      /**
 401       * Validate that the active component is valid
 402       *
 403       * @param   string  $option  URL Option of the component. Example: com_content
 404       *
 405       * @return  boolean
 406       *
 407       * @since   3.2
 408       */
 409      protected function validComponent($option = null)
 410      {
 411          // By default we will validate the active component
 412          $component = $option ?? $this->options->get('component', null);
 413  
 414          // Valid option format
 415          if (!empty($component) && substr_count($component, 'com_')) {
 416              // Latest check: component exists and is enabled
 417              return ComponentHelper::isEnabled($component);
 418          }
 419  
 420          return false;
 421      }
 422  
 423      /**
 424       * Method to change the component where search for layouts
 425       *
 426       * @param   string  $option  URL Option of the component. Example: com_content
 427       *
 428       * @return  mixed  Component option string | null for none
 429       *
 430       * @since   3.2
 431       */
 432      public function setComponent($option)
 433      {
 434          $component = null;
 435  
 436          switch ((string) $option) {
 437              case 'none':
 438                  $component = null;
 439                  break;
 440  
 441              case 'auto':
 442                  $component = ApplicationHelper::getComponentName();
 443                  break;
 444  
 445              default:
 446                  $component = $option;
 447                  break;
 448          }
 449  
 450          // Extra checks
 451          if (!$this->validComponent($component)) {
 452              $component = null;
 453          }
 454  
 455          $this->options->set('component', $component);
 456  
 457          // Refresh include paths
 458          $this->clearIncludePaths();
 459      }
 460  
 461      /**
 462       * Function to initialise the application client
 463       *
 464       * @param   mixed  $client  Frontend: 'site' or 0 | Backend: 'admin' or 1
 465       *
 466       * @return  void
 467       *
 468       * @since   3.2
 469       */
 470      public function setClient($client)
 471      {
 472          // Force string conversion to avoid unexpected states
 473          switch ((string) $client) {
 474              case 'site':
 475              case '0':
 476                  $client = 0;
 477                  break;
 478  
 479              case 'admin':
 480              case '1':
 481                  $client = 1;
 482                  break;
 483  
 484              default:
 485                  $client = (int) Factory::getApplication()->isClient('administrator');
 486                  break;
 487          }
 488  
 489          $this->options->set('client', $client);
 490  
 491          // Refresh include paths
 492          $this->clearIncludePaths();
 493      }
 494  
 495      /**
 496       * Set the active layout id
 497       *
 498       * @param   string  $layoutId  Layout identifier
 499       *
 500       * @return  self
 501       *
 502       * @since   3.5
 503       */
 504      public function setLayoutId($layoutId)
 505      {
 506          $this->layoutId = $layoutId;
 507          $this->fullPath = null;
 508  
 509          return $this;
 510      }
 511  
 512      /**
 513       * Get the default array of include paths
 514       *
 515       * @return  array
 516       *
 517       * @since   3.5
 518       */
 519      public function getDefaultIncludePaths()
 520      {
 521          // Get the template
 522          $template = Factory::getApplication()->getTemplate(true);
 523  
 524          // Reset includePaths
 525          $paths = array();
 526  
 527          // (1 - highest priority) Received a custom high priority path
 528          if ($this->basePath !== null) {
 529              $paths[] = rtrim($this->basePath, DIRECTORY_SEPARATOR);
 530          }
 531  
 532          // Component layouts & overrides if exist
 533          $component = $this->options->get('component', null);
 534  
 535          if (!empty($component)) {
 536              // (2) Component template overrides path
 537              $paths[] = JPATH_THEMES . '/' . $template->template . '/html/layouts/' . $component;
 538  
 539              if (!empty($template->parent)) {
 540                  // (2.a) Component template overrides path for an inherited template using the parent
 541                  $paths[] = JPATH_THEMES . '/' . $template->parent . '/html/layouts/' . $component;
 542              }
 543  
 544              // (3) Component path
 545              if ($this->options->get('client') == 0) {
 546                  $paths[] = JPATH_SITE . '/components/' . $component . '/layouts';
 547              } else {
 548                  $paths[] = JPATH_ADMINISTRATOR . '/components/' . $component . '/layouts';
 549              }
 550          }
 551  
 552          // (4) Standard Joomla! layouts overridden
 553          $paths[] = JPATH_THEMES . '/' . $template->template . '/html/layouts';
 554  
 555          if (!empty($template->parent)) {
 556              // (4.a) Component template overrides path for an inherited template using the parent
 557              $paths[] = JPATH_THEMES . '/' . $template->parent . '/html/layouts';
 558          }
 559  
 560          // (5 - lower priority) Frontend base layouts
 561          $paths[] = JPATH_ROOT . '/layouts';
 562  
 563          return $paths;
 564      }
 565  
 566      /**
 567       * Set the include paths to search for layouts
 568       *
 569       * @param   array  $paths  Array with paths to search in
 570       *
 571       * @return  self
 572       *
 573       * @since   3.5
 574       */
 575      public function setIncludePaths($paths)
 576      {
 577          $this->includePaths = (array) $paths;
 578  
 579          return $this;
 580      }
 581  
 582      /**
 583       * Set suffixes to search layouts
 584       *
 585       * @param   mixed  $suffixes  String with a single suffix or 'auto' | 'none' or array of suffixes
 586       *
 587       * @return  self
 588       *
 589       * @since   3.5
 590       */
 591      public function setSuffixes(array $suffixes)
 592      {
 593          $this->options->set('suffixes', $suffixes);
 594  
 595          return $this;
 596      }
 597  
 598      /**
 599       * Render a layout with the same include paths & options
 600       *
 601       * @param   string  $layoutId     The identifier for the sublayout to be searched in a subfolder with the name of the current layout
 602       * @param   mixed   $displayData  Data to be rendered
 603       *
 604       * @return  string  The necessary HTML to display the layout
 605       *
 606       * @since   3.2
 607       */
 608      public function sublayout($layoutId, $displayData)
 609      {
 610          // Sublayouts are searched in a subfolder with the name of the current layout
 611          if (!empty($this->layoutId)) {
 612              $layoutId = $this->layoutId . '.' . $layoutId;
 613          }
 614  
 615          $sublayout = new static($layoutId, $this->basePath, $this->options);
 616          $sublayout->includePaths = $this->includePaths;
 617  
 618          return $sublayout->render($displayData);
 619      }
 620  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer