MODX Revolution PHP Cross Reference Content Management Systems

Source: /setup/includes/modinstall.class.php - 631 lines - 23306 bytes - Summary - Text - Print

Description: Common classes for the MODX installation and provisioning services.

   1  <?php
   2  /*
   3   * MODX Revolution
   4   *
   5   * Copyright 2006-2014 by MODX, LLC.
   6   * All rights reserved.
   7   *
   8   * This program is free software; you can redistribute it and/or modify it under
   9   * the terms of the GNU General Public License as published by the Free Software
  10   * Foundation; either version 2 of the License, or (at your option) any later
  11   * version.
  12   *
  13   * This program is distributed in the hope that it will be useful, but WITHOUT
  14   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16   * details.
  17   *
  18   * You should have received a copy of the GNU General Public License along with
  19   * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  20   * Place, Suite 330, Boston, MA 02111-1307 USA
  21   */
  22  
  23  /**
  24   * Common classes for the MODX installation and provisioning services.
  25   *
  26   * @package setup
  27   */
  28  /**
  29   * Provides common functionality and data for installation and provisioning.
  30   *
  31   * @package setup
  32   */
  33  class modInstall {
  34      const MODE_NEW = 0;
  35      const MODE_UPGRADE_REVO = 1;
  36      const MODE_UPGRADE_EVO = 2;
  37      const MODE_UPGRADE_REVO_ADVANCED = 3;
  38  
  39      /** @var xPDO $xpdo */
  40      public $xpdo = null;
  41      public $options = array ();
  42      /** @var modInstallRequest $request */
  43      public $request = null;
  44      /** @var modInstallSettings $settings */
  45      public $settings = null;
  46      /** @var modInstallLexicon $lexicon */
  47      public $lexicon = null;
  48      /** @var modInstallTest $test */
  49      public $test;
  50      /** @var modInstallDriver $driver */
  51      public $driver;
  52      /** @var modInstallRunner $runner */
  53      public $runner;
  54      /** @var array $config */
  55      public $config = array ();
  56      public $action = '';
  57      public $finished = false;
  58  
  59      /**
  60       * The constructor for the modInstall object.
  61       *
  62       * @constructor
  63       * @param array $options An array of configuration options.
  64       */
  65      function __construct(array $options = array()) {
  66          if (isset ($_REQUEST['action'])) {
  67              $this->action = $_REQUEST['action'];
  68          }
  69          if (is_array($options)) {
  70              $this->options = $options;
  71          }
  72      }
  73  
  74      /**
  75       * Load a class file for setup
  76       * @param string $class The name of the class to load
  77       * @param string $path The path to load the class from
  78       * @return array|bool
  79       */
  80      public function loadClass($class,$path = '') {
  81          $classFile = str_replace('.', '/', strtolower($class));
  82          $className = explode('.',$class);
  83          $className = array_reverse($className);
  84          $className = $className[0];
  85  
  86          if (empty($path)) {
  87              $path = strtr(realpath(MODX_SETUP_PATH.'includes'),'\\','/').'/';
  88          }
  89  
  90          $classPath = $path.$classFile.'.class.php';
  91          $included = require_once $classPath;
  92          return $included ? $className : false;
  93      }
  94  
  95      /**
  96       * Return a service class instance
  97       * @param string $name
  98       * @param string $class
  99       * @param string $path
 100       * @param array $config
 101       * @return Object|null
 102       */
 103      public function getService($name,$class,$path = '',array $config = array()) {
 104          if (empty($this->$name)) {
 105              $className = $this->loadClass($class,$path);
 106              if (!empty($className)) {
 107                  $this->$name = new $className($this,$config);
 108              } else {
 109                  $this->_fatalError($this->lexicon('service_err_nf',array(
 110                      'name' => $name,
 111                      'class' => $class,
 112                      'path' => $path,
 113                  )));
 114              }
 115          }
 116          return $this->$name;
 117      }
 118  
 119      /**
 120       * Load settings class
 121       *
 122       * @access public
 123       * @param string $class The settings class to load.
 124       * @param string $path
 125       * @return modInstallSettings
 126       */
 127      public function loadSettings($class = 'modInstallSettings',$path = '') {
 128          if (empty($this->settings)) {
 129              $className = $this->loadClass($class,$path);
 130              if (!empty($className)) {
 131                  $this->settings = new $className($this);
 132              } else {
 133                  $this->_fatalError($this->lexicon('settings_handler_err_nf',array('path' => $className)));
 134              }
 135          }
 136          return $this->settings;
 137      }
 138  
 139      /**
 140       * Shortcut method for modInstallLexicon::get. {@see modInstallLexicon::get}
 141       *
 142       * @param string $key
 143       * @param array $placeholders
 144       * @return string
 145       */
 146      public function lexicon($key,array $placeholders = array()) {
 147          return $this->lexicon->get($key,$placeholders);
 148      }
 149  
 150      /**
 151       * Get an xPDO connection to the database.
 152       *
 153       * @param int $mode
 154       * @return xPDO A copy of the xpdo object.
 155       */
 156      public function getConnection($mode = 0) {
 157          if ($this->settings && empty($mode)) $mode = (int)$this->settings->get('installmode');
 158          if (empty($mode)) $mode = modInstall::MODE_NEW;
 159          if ($mode === modInstall::MODE_UPGRADE_REVO) {
 160              $errors = array ();
 161              $this->xpdo = $this->_modx($errors);
 162          } else if (!is_object($this->xpdo)) {
 163              $options = array();
 164              if ($this->settings->get('new_folder_permissions')) $options['new_folder_permissions'] = $this->settings->get('new_folder_permissions');
 165              if ($this->settings->get('new_file_permissions')) $options['new_file_permissions'] = $this->settings->get('new_file_permissions');
 166              $this->xpdo = $this->_connect(
 167                  $this->settings->get('database_dsn')
 168                  ,$this->settings->get('database_user')
 169                  ,$this->settings->get('database_password')
 170                  ,$this->settings->get('table_prefix')
 171                  ,$options
 172               );
 173  
 174              if (!($this->xpdo instanceof xPDO)) { return $this->xpdo; }
 175  
 176              $this->xpdo->setOption('cache_path',MODX_CORE_PATH . 'cache/');
 177  
 178              if ($mode === modInstall::MODE_UPGRADE_REVO_ADVANCED) {
 179                  if ($this->xpdo->connect()) {
 180                      $errors = array ();
 181                      $this->xpdo = $this->_modx($errors);
 182                  } else {
 183                      return $this->lexicon('db_err_connect_upgrade');
 184                  }
 185              }
 186          }
 187          if (is_object($this->xpdo) && $this->xpdo instanceof xPDO) {
 188              $this->xpdo->setLogTarget(array(
 189                  'target' => 'FILE',
 190                  'options' => array(
 191                      'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y-%m-%dT%H.%M.%S').'.log'
 192                  )
 193              ));
 194              $this->xpdo->setLogLevel(xPDO::LOG_LEVEL_ERROR);
 195              $this->xpdo->setPackage('modx', MODX_CORE_PATH . 'model/', $this->settings->get('table_prefix'));
 196          }
 197          return $this->xpdo;
 198      }
 199  
 200      /**
 201       * Load distribution-specific test handlers
 202       *
 203       * @param string $class
 204       * @param string $path
 205       * @param array $config
 206       * @return modInstallTest|void
 207       */
 208      public function loadTestHandler($class = 'test.modInstallTest',$path = '',array $config = array()) {
 209          $className = $this->loadClass($class,$path);
 210          if (!empty($className)) {
 211              $this->lexicon->load('test');
 212  
 213              $distributionClass = 'test.'.$className.ucfirst(trim(MODX_SETUP_KEY, '@'));
 214              $distributionClassName = $this->loadClass($distributionClass,$path);
 215              if (empty($distributionClassName)) {
 216                  $this->_fatalError($this->lexicon('test_version_class_nf',array('path' => $distributionClass)));
 217              }
 218              $this->test = new $distributionClassName($this);
 219          } else {
 220              $this->_fatalError($this->lexicon('test_class_nf',array('path' => $path)));
 221          }
 222          return $this->test;
 223      }
 224  
 225      /**
 226       * Perform a series of pre-installation tests.
 227       *
 228       * @param integer $mode The install mode.
 229       * @param string $testClass The class to run tests with
 230       * @param string $testClassPath
 231       * @return array An array of result messages collected during the process.
 232       */
 233      public function test($mode = modInstall::MODE_NEW,$testClass = 'test.modInstallTest',$testClassPath = '') {
 234          $this->loadTestHandler($testClass,$testClassPath);
 235          return $this->test->run($mode);
 236      }
 237  
 238      /**
 239       * Verify that the modX class can be initialized.
 240       *
 241       * @return array An array of error messages collected during the process.
 242       */
 243      public function verify() {
 244          $errors = array ();
 245          $modx = $this->_modx($errors);
 246          if (is_object($modx) && $modx instanceof modX) {
 247              if ($modx->getCacheManager()) {
 248                  $modx->cacheManager->refresh();
 249              }
 250          }
 251          return $errors;
 252      }
 253  
 254      /**
 255       * Cleans up after install.
 256       *
 257       * TODO: implement this function to cleanup any temporary files
 258       * @param array $options
 259       * @return array
 260       */
 261      public function cleanup(array $options = array ()) {
 262          $errors = array();
 263          $modx = $this->_modx($errors);
 264          if (empty($modx) || !($modx instanceof modX)) {
 265              $errors['modx_class'] = $this->lexicon('modx_err_instantiate');
 266              return $errors;
 267          }
 268  
 269          /* create the directories for Package Management */
 270          /** @var modCacheManager $cacheManager */
 271          $cacheManager = $modx->getCacheManager();
 272          $directoryOptions = array(
 273              'new_folder_permissions' => $modx->getOption('new_folder_permissions',null,0775),
 274          );
 275  
 276          /* create assets/ */
 277          $assetsPath = $this->settings->get('assets_path',$this->settings->get('web_path',$modx->getOption('base_path')).'assets/');
 278          if (!is_dir($assetsPath)) {
 279              $cacheManager->writeTree($assetsPath,$directoryOptions);
 280          }
 281          if (!is_dir($assetsPath) || !$this->is_writable2($assetsPath)) {
 282              $errors['assets_not_created'] = str_replace('[[+path]]',$assetsPath,$this->lexicon('setup_err_assets'));
 283          }
 284          unset($assetsPath);
 285  
 286          /* create assets/components/ */
 287          $assetsCompPath = $this->settings->get('assets_path',$this->settings->get('web_path',$modx->getOption('base_path')).'assets/').'components/';
 288          if (!is_dir($assetsCompPath)) {
 289              $cacheManager->writeTree($assetsCompPath,$directoryOptions);
 290          }
 291          if (!is_dir($assetsCompPath) || !$this->is_writable2($assetsCompPath)) {
 292              $errors['assets_comp_not_created'] = str_replace('[[+path]]',$assetsCompPath,$this->lexicon('setup_err_assets_comp'));
 293          }
 294          unset($assetsCompPath);
 295  
 296          /* create core/components/ */
 297          $coreCompPath = $this->settings->get('core_path',$modx->getOption('core_path',null,MODX_CORE_PATH)).'components/';
 298          if (!is_dir($coreCompPath)) {
 299              $cacheManager->writeTree($coreCompPath,$directoryOptions);
 300          }
 301          if (!is_dir($coreCompPath) || !$this->is_writable2($coreCompPath)) {
 302              $errors['core_comp_not_created'] = str_replace('[[+path]]',$coreCompPath,$this->lexicon('setup_err_core_comp'));
 303          }
 304          unset($coreCompPath);
 305  
 306          return $errors;
 307      }
 308  
 309      /**
 310       * Removes the setup directory
 311       *
 312       * @access public
 313       * @param array $options
 314       * @return array
 315       */
 316      public function removeSetupDirectory(array $options = array()) {
 317          $errors = array();
 318  
 319          $modx = $this->_modx($errors);
 320          if ($modx) {
 321              /** @var modCacheManager $cacheManager */
 322              $cacheManager = $modx->getCacheManager();
 323              if ($cacheManager) {
 324                  $setupPath = $modx->getOption('base_path').'setup/';
 325                  if (!$cacheManager->deleteTree($setupPath,true,false,false)) {
 326                      $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('setup_err_remove'));
 327                  }
 328              } else {
 329                  $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('cache_manager_err'));
 330              }
 331          } else {
 332              $modx->log(modX::LOG_LEVEL_ERROR,$this->lexicon('modx_object_err'));
 333          }
 334          return $errors;
 335      }
 336  
 337      /**
 338       * Generates a random universal unique ID for identifying modx installs
 339       *
 340       * @return string A universally unique ID
 341       */
 342      public function generateUUID() {
 343          srand(intval(microtime(true) * 1000));
 344          $b = md5(uniqid(rand(),true),true);
 345          $b[6] = chr((ord($b[6]) & 0x0F) | 0x40);
 346          $b[8] = chr((ord($b[8]) & 0x3F) | 0x80);
 347          return implode('-',unpack('H8a/H4b/H4c/H4d/H12e',$b));
 348      }
 349  
 350      /**
 351       * Installs a transport package.
 352       *
 353       * @param string $pkg The package signature.
 354       * @param array $attributes An array of installation attributes.
 355       * @return array An array of error messages collected during the process.
 356       */
 357      public function installPackage($pkg, array $attributes = array ()) {
 358          $errors = array ();
 359  
 360          /* instantiate the modX class */
 361          if (@ require_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
 362              $modx = new modX(MODX_CORE_PATH . 'config/');
 363              if (!is_object($modx) || !($modx instanceof modX)) {
 364                  $errors[] = '<p>'.$this->lexicon('modx_err_instantiate').'</p>';
 365              } else {
 366                  /* try to initialize the mgr context */
 367                  $modx->initialize('mgr');
 368                  if (!$modx->isInitialized()) {
 369                      $errors[] = '<p>'.$this->lexicon('modx_err_instantiate_mgr').'</p>';
 370                  } else {
 371                      $loaded = $modx->loadClass('transport.xPDOTransport', XPDO_CORE_PATH, true, true);
 372                      if (!$loaded)
 373                          $errors[] = '<p>'.$this->lexicon('transport_class_err_load').'</p>';
 374  
 375                      $packageDirectory = MODX_CORE_PATH . 'packages/';
 376                      $packageState = (isset ($attributes[xPDOTransport::PACKAGE_STATE]) ? $attributes[xPDOTransport::PACKAGE_STATE] : xPDOTransport::STATE_PACKED);
 377                      $package = xPDOTransport :: retrieve($modx, $packageDirectory . $pkg . '.transport.zip', $packageDirectory, $packageState);
 378                      if ($package) {
 379                          if (!$package->install($attributes)) {
 380                              $errors[] = '<p>'.$this->lexicon('package_err_install',array('package' => $pkg)).'</p>';
 381                          } else {
 382                              $modx->log(xPDO::LOG_LEVEL_INFO,$this->lexicon('package_installed',array('package' => $pkg)));
 383                          }
 384                      } else {
 385                          $errors[] = '<p>'.$this->lexicon('package_err_nf',array('package' => $pkg)).'</p>';
 386                      }
 387                  }
 388              }
 389          } else {
 390              $errors[] = '<p>'.$this->lexicon('modx_class_err_nf').'</p>';
 391          }
 392  
 393          return $errors;
 394      }
 395  
 396      /**
 397       * Gets the manager login URL.
 398       *
 399       * @return string The URL of the installed manager context.
 400       */
 401      public function getManagerLoginUrl() {
 402          $url = '';
 403  
 404          /* instantiate the modX class */
 405          if (@ require_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
 406              $modx = new modX(MODX_CORE_PATH . 'config/');
 407              if (is_object($modx) && $modx instanceof modX) {
 408                  /* try to initialize the mgr context */
 409                  $modx->initialize('mgr');
 410                  $url = MODX_URL_SCHEME.$modx->getOption('http_host').$modx->getOption('manager_url');
 411              }
 412          }
 413          return $url;
 414      }
 415  
 416      /**
 417       * Determines the possible install modes.
 418       *
 419       * @access public
 420       * @return integer One of three possible mode indicators:<ul>
 421       * <li>0 = new install only</li>
 422       * <li>1 = new OR upgrade from older versions of MODX Revolution</li>
 423       * <li>2 = new OR upgrade from MODX Evolution</li>
 424       * </ul>
 425       */
 426      public function getInstallMode() {
 427          $mode = modInstall::MODE_NEW;
 428          if (isset ($_POST['installmode'])) {
 429              $mode = intval($_POST['installmode']);
 430          } else {
 431              global $dbase;
 432              if (file_exists(MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php')) {
 433                  /* Include the file so we can test its validity */
 434                  $included = @ include (MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php');
 435                  $mode = ($included && isset ($dbase)) ? modInstall::MODE_UPGRADE_REVO : modInstall::MODE_NEW;
 436              }
 437              if (!$mode && file_exists(MODX_INSTALL_PATH . 'manager/includes/config.inc.php')) {
 438                  $included = @ include (MODX_INSTALL_PATH . 'manager/includes/config.inc.php');
 439                  $mode = ($included && isset ($dbase)) ? modInstall::MODE_UPGRADE_EVO : modInstall::MODE_NEW;
 440              }
 441          }
 442          return $mode;
 443      }
 444  
 445      /**
 446       * Creates the database connection for the installation process.
 447       *
 448       * @access private
 449       * @return xPDO The xPDO instance to be used by the installation.
 450       */
 451      public function _connect($dsn, $user = '', $password = '', $prefix = '', array $options = array()) {
 452          if (include_once (MODX_CORE_PATH . 'xpdo/xpdo.class.php')) {
 453              $this->xpdo = new xPDO($dsn, $user, $password, array_merge(array(
 454                      xPDO::OPT_CACHE_PATH => MODX_CORE_PATH . 'cache/',
 455                      xPDO::OPT_TABLE_PREFIX => $prefix,
 456                      xPDO::OPT_SETUP => true,
 457                  ), $options),
 458                  array(PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT)
 459              );
 460              $this->xpdo->setLogTarget(array(
 461                  'target' => 'FILE',
 462                  'options' => array(
 463                      'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y%m%dT%H%M%S') . '.log'
 464                  )
 465              ));
 466              $this->xpdo->setLogLevel(xPDO::LOG_LEVEL_ERROR);
 467              return $this->xpdo;
 468          } else {
 469              return $this->lexicon('xpdo_err_nf', array('path' => MODX_CORE_PATH.'xpdo/xpdo.class.php'));
 470          }
 471      }
 472  
 473      /**
 474       * Instantiate an existing modX configuration.
 475       *
 476       * @param array &$errors An array in which error messages are collected.
 477       * @return modX|null The modX instance, or null if it could not be instantiated.
 478       */
 479      private function _modx(array & $errors) {
 480          $modx = null;
 481  
 482          /* to validate installation, instantiate the modX class and run a few tests */
 483          if (include_once (MODX_CORE_PATH . 'model/modx/modx.class.php')) {
 484              $modx = new modX(MODX_CORE_PATH . 'config/', array(
 485                  xPDO::OPT_SETUP => true,
 486              ));
 487              if (!is_object($modx) || !($modx instanceof modX)) {
 488                  $errors[] = '<p>'.$this->lexicon('modx_err_instantiate').'</p>';
 489              } else {
 490                  $modx->setLogTarget(array(
 491                      'target' => 'FILE',
 492                      'options' => array(
 493                          'filename' => 'install.' . MODX_CONFIG_KEY . '.' . strftime('%Y%m%dT%H%M%S') . '.log'
 494                      )
 495                  ));
 496  
 497                  /* try to initialize the mgr context */
 498                  $modx->initialize('mgr');
 499                  if (!$modx->isInitialized()) {
 500                      $errors[] = '<p>'.$this->lexicon('modx_err_instantiate_mgr').'</p>';
 501                  }
 502              }
 503          } else {
 504              $errors[] = '<p>'.$this->lexicon('modx_class_err_nf').'</p>';
 505          }
 506  
 507          return $modx;
 508      }
 509  
 510      /**
 511       * Finds the core directory, if possible. If core cannot be found, loads the
 512       * findcore controller.
 513       *
 514       * @return Returns true if core directory is found.
 515       */
 516      public function findCore() {
 517          $exists = false;
 518          if (defined('MODX_CORE_PATH') && file_exists(MODX_CORE_PATH) && is_dir(MODX_CORE_PATH)) {
 519              if (file_exists(MODX_CORE_PATH . 'xpdo/xpdo.class.php') && file_exists(MODX_CORE_PATH . 'model/modx/modx.class.php')) {
 520                  $exists = true;
 521              }
 522          }
 523          if (!$exists) {
 524              include(MODX_SETUP_PATH . 'templates/findcore.php');
 525              die();
 526          }
 527          return $exists;
 528      }
 529  
 530      /**
 531       * Does all the pre-load checks, before setup loads.
 532       *
 533       * @access public
 534       */
 535      public function doPreloadChecks() {
 536          $this->lexicon->load('preload');
 537          $errors= array();
 538  
 539          if (!extension_loaded('pdo')) {
 540              $errors[] = $this->lexicon('preload_err_pdo');
 541          }
 542          if (!file_exists(MODX_CORE_PATH) || !is_dir(MODX_CORE_PATH)) {
 543              $errors[] = $this->lexicon('preload_err_core_path');
 544          }
 545          if (!file_exists(MODX_CORE_PATH . 'cache/') || !is_dir(MODX_CORE_PATH . 'cache/') || !$this->is_writable2(MODX_CORE_PATH . 'cache/')) {
 546              $errors[] = $this->lexicon('preload_err_cache',array('path' => MODX_CORE_PATH));
 547          }
 548  
 549          if (!empty($errors)) {
 550              $this->_fatalError($errors);
 551          }
 552      }
 553  
 554      /**
 555       * Outputs a fatal error message and then dies.
 556       *
 557       * @param string|array $errors A string or array of errors
 558       * @return void
 559       */
 560      public function _fatalError($errors) {
 561          $output = '<html><head><title></title></head><body><h1>'.$this->lexicon('fatal_error').'</h1><ul>';
 562          if (is_array($errors)) {
 563              foreach ($errors as $error) {
 564                  $output .= '<li>'.$error.'</li>';
 565              }
 566          } else {
 567              $output .= '<li>'.$errors.'</li>';
 568          }
 569          $output .= '</ul></body></html>';
 570          die($output);
 571      }
 572  
 573      /**
 574       * Custom is_writable function to test on problematic servers
 575       *
 576       * @param string $path
 577       * @return boolean True if write was successful
 578       */
 579      public function is_writable2($path) {
 580          $written = false;
 581          if (!is_string($path)) return false;
 582  
 583          /* if is file get parent dir */
 584          if (is_file($path)) { $path = dirname($path) . '/'; }
 585  
 586          /* ensure / at end, translate \ to / for windows */
 587          if (substr($path,strlen($path)-1) != '/') { $path .= '/'; }
 588          $path = strtr($path,'\\','/');
 589  
 590          /* get test file */
 591          $filePath = $path.uniqid().'.cache.php';
 592  
 593          /* attempt to create test file */
 594          $fp = @fopen($filePath,'w');
 595          if ($fp === false || !file_exists($filePath)) return false;
 596  
 597          /* attempt to write to test file */
 598          $written = @fwrite($fp,'<?php echo "test";');
 599          if (!$written) { /* if fails try to delete it */
 600              @fclose($fp);
 601              @unlink($filePath);
 602              return false;
 603          }
 604  
 605          /* attempt to delete test file */
 606          @fclose($fp);
 607          $written = @unlink($filePath);
 608  
 609          return $written;
 610      }
 611  
 612      /**
 613       * Loads the correct database driver for this environment.
 614       *
 615       * @param string $path
 616       * @return boolean True if successful.
 617       */
 618      public function loadDriver($path = '') {
 619          $this->loadSettings();
 620  
 621          /* db specific driver */
 622          $class = 'drivers.modInstallDriver_'.strtolower($this->settings->get('database_type','mysql'));
 623          $className = $this->loadClass($class,$path);
 624          if (!empty($className)) {
 625              $this->driver = new $className($this);
 626          } else {
 627              $this->_fatalError($this->lexicon('driver_class_err_nf',array('path' => $class)));
 628          }
 629          return !empty($className);
 630      }
 631  }

title

Description

title

Description

title

Description

title

title

Body