Created
January 16, 2014 06:07
-
-
Save sun/8450528 to your computer and use it in GitHub Desktop.
Singleton
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Utility\Site. | |
*/ | |
namespace Drupal\Core\Utility; | |
use Drupal\Component\Utility\Settings; | |
/** | |
* A utility class for easy access to the site path. | |
*/ | |
class Site { | |
/** | |
* The absolute path to the Drupal root directory. | |
* | |
* @var string | |
*/ | |
private $root; | |
/** | |
* The relative path to the site directory. | |
* | |
* May be an empty string, in case the site directory is the root directory. | |
* | |
* @var string | |
*/ | |
private $path; | |
/** | |
* Whether the Site singleton was instantiated by the installer. | |
* | |
* @var bool | |
*/ | |
private $isInstaller; | |
/** | |
* The original Site instance of the test runner during test execution. | |
* | |
* @see \Drupal\Core\Utility\Site::setUpTest() | |
* @see \Drupal\Core\Utility\Site::tearDownTest() | |
* | |
* @var \Drupal\Core\Utility\Site | |
*/ | |
private $original; | |
/** | |
* The Site singleton instance. | |
* | |
* @var \Drupal\Core\Utility\Site | |
*/ | |
private static $instance; | |
/** | |
* Initializes the Site singleton. | |
* | |
* @param string $root_directory | |
* The root directory to use for absolute paths; i.e., DRUPAL_ROOT. | |
* @param array $sites | |
* (optional) A multi-site mapping, as defined in settings.php. | |
* @param string $custom_path | |
* (optional) An explicit site path to set; skipping site negotiation. | |
* This can be defined as $conf_path in the root /settings.php file. | |
* | |
* @see drupal_settings_initialize() | |
*/ | |
public static function init($root_directory, array $sites = NULL, $custom_path = NULL) { | |
if (!isset(self::$instance)) { | |
new self($root_directory); | |
} | |
// Only the installer environment is allowed instantiate the Site singleton | |
// prior to drupal_settings_initialize(). | |
// @see initInstaller() | |
elseif (!self::$instance->isInstaller()) { | |
throw new \BadMethodCallException('Site path is initialized already.'); | |
} | |
// Force-override the site directory in tests. | |
if ($test_prefix = drupal_valid_test_ua()) { | |
$custom_path = 'sites/simpletest/' . substr($test_prefix, 10); | |
} | |
self::$instance->initializePath($sites, $custom_path); | |
} | |
/** | |
* Initializes the Site singleton for the early installer environment. | |
* | |
* The installer uses this function to prime the site directory path very | |
* early in the installer environmnt. This allows the application to be | |
* installed into a new and empty site directory, which does not contain a | |
* settings.php yet. | |
* | |
* @param string $root_directory | |
* The root directory to use for absolute paths; i.e., DRUPAL_ROOT. | |
* | |
* @see install_begin_request() | |
*/ | |
public static function initInstaller($root_directory) { | |
// WebTestBase::setUp() invokes Site::setUpTest() prior to invoking the | |
// non-interactive installer, in order to override the singleton and prime | |
// a custom site directory. | |
if (drupal_valid_test_ua()) { | |
return; | |
} | |
if (isset(self::$instance)) { | |
throw new \BadMethodCallException('Site path is initialized already.'); | |
} | |
// Set a global state flag to denote that we are operating in the special | |
// installer environment. initializePath() is unnecessary, as init() will be | |
// called by drupal_settings_initialize(), possibly passing new values from | |
// the newly created settings.php file. | |
new self($root_directory, TRUE); | |
} | |
/** | |
* Re-initializes the Site singleton for a test run. | |
* | |
* Called by WebTestBase::setUp() prior to invoking the non-interactive | |
* installer for a test. | |
* | |
* @param string $root_directory | |
* The root directory to use for absolute paths; i.e., DRUPAL_ROOT. | |
* @param string $custom_path | |
* The relative site directory path to use during the test run; e.g., | |
* 'sites/simpletest/123456'. | |
*/ | |
public static function setUpTest($root_directory, $custom_path) { | |
if (!drupal_valid_test_ua()) { | |
throw new \BadMethodCallException('Site is not executing a test.'); | |
} | |
if (!file_exists($root_directory . '/' . $custom_path)) { | |
throw new \InvalidArgumentException("Test site directory '$custom_path' does not exist."); | |
} | |
new self($root_directory, TRUE, $custom_path); | |
} | |
/** | |
* Reverts the Site singleton to the original after a test run. | |
*/ | |
public static function tearDownTest() { | |
if (!isset(self::$instance->original)) { | |
throw new \RuntimeException('No original Site to revert to. Missing invocation of Site::setUpTest()?'); | |
} | |
self::$instance = self::$instance->original; | |
} | |
/** | |
* Constructs the Site singleton. | |
*/ | |
private function __construct($root_directory, $is_installer = FALSE, $custom_path = NULL) { | |
if (isset(self::$instance->original)) { | |
throw new \RuntimeException('Site is overridden for a test already. Duplicate invocation of Site::setUpTest()?'); | |
} | |
if (isset(self::$instance)) { | |
$this->original = clone self::$instance; | |
} | |
$this->root = $root_directory; | |
$this->isInstaller = $is_installer; | |
$this->path = $custom_path; | |
self::$instance = $this; | |
} | |
/** | |
* Returns whether the Site singleton was instantiated for the installer. | |
* | |
* @todo Leverage this to eliminate drupal_installation_attempted()? | |
*/ | |
private function isInstaller() { | |
return $this->isInstaller; | |
} | |
/** | |
* Initializes the site path. | |
* | |
* @param array $sites | |
* (optional) A multi-site mapping, as defined in settings.php. | |
* @param string $custom_path | |
* (optional) An explicit site path to set; skipping site negotiation. | |
*/ | |
private function initializePath(array $sites = NULL, $custom_path = NULL) { | |
// When executing the non-interactive installer in the parent site/ | |
// test-runner, WebTestBase::setUp() primes the site directory path via | |
// Site::setUpTest() already. | |
// @todo Remove this case by adding a 'site' parameter to drupal_install(). | |
if (isset($this->path)) { | |
return; | |
} | |
// An explicitly defined $conf_path in /settings.php takes precedence. | |
if (isset($custom_path)) { | |
$this->path = $custom_path; | |
} | |
// If the multi-site functionality was enabled in /settings.php, discover | |
// the path for the current site. | |
// $sites just needs to be defined; an explicit mapping is not required. | |
elseif (isset($sites)) { | |
$this->path = $this->determinePath($sites, !$this->isInstaller()); | |
} | |
// If the multi-site functionality is not enabled, the Drupal root | |
// directory is the site directory. | |
else { | |
$this->path = ''; | |
} | |
} | |
/** | |
* Finds the appropriate configuration directory for a given host and path. | |
* | |
* Finds a matching configuration directory file by stripping the website's | |
* hostname from left to right and pathname from right to left. By default, | |
* the directory must contain a 'settings.php' file for it to match. If the | |
* parameter $require_settings is set to FALSE, then a directory without a | |
* 'settings.php' file will match as well. The first configuration | |
* file found will be used and the remaining ones will be ignored. | |
* | |
* The settings.php file can define aliases in an associative array named | |
* $sites. For example, to create a directory alias for | |
* http://www.drupal.org:8080/mysite/test whose configuration file is in | |
* sites/example.com, the array should be defined as: | |
* @code | |
* $sites = array( | |
* '8080.www.drupal.org.mysite.test' => 'example.com', | |
* ); | |
* @endcode | |
* | |
* @see default.settings.php | |
* | |
* @param array $sites | |
* A multi-site mapping, as defined in settings.php. | |
* @param bool $require_settings | |
* Only configuration directories with an existing settings.php file | |
* will be recognized. Defaults to TRUE. During initial installation, | |
* this is set to FALSE so that Drupal can detect a matching directory, | |
* then create a new settings.php file in it. | |
* | |
* @return string | |
* The path of the matching configuration directory. May be an empty string, | |
* in case the site configuration directory is the root directory. | |
* | |
* @todo Inject a Request object in instead of relying on globals? | |
*/ | |
private function determinePath(array $sites, $require_settings) { | |
// The hostname and optional port number, e.g. "www.example.com" or | |
// "www.example.com:8080". | |
$http_host = $_SERVER['HTTP_HOST']; | |
// The part of the URL following the hostname, including the leading slash. | |
$script_name = $_SERVER['SCRIPT_NAME'] ?: $_SERVER['SCRIPT_FILENAME']; | |
$uri = explode('/', $script_name); | |
$server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.'))))); | |
for ($i = count($uri) - 1; $i > 0; $i--) { | |
for ($j = count($server); $j > 0; $j--) { | |
$dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); | |
// Check for an alias in $sites. | |
if (isset($sites[$dir])) { | |
$dir = $sites[$dir]; | |
// A defined site alias from /settings.php should be valid. | |
// @todo Even skip the settings.php check? | |
if (!$require_settings) { | |
return "sites/$dir"; | |
} | |
} | |
if ($require_settings) { | |
if (file_exists($this->root . '/sites/' . $dir . '/settings.php')) { | |
return "sites/$dir"; | |
} | |
} | |
elseif (file_exists($this->root . '/sites/' . $dir)) { | |
return "sites/$dir"; | |
} | |
} | |
} | |
return ''; | |
} | |
/** | |
* Prefixes a given filepath with the site directory, if any. | |
* | |
* @param string $filepath | |
* The filepath to prefix. | |
* | |
* @return string | |
* The prefixed filepath. | |
*/ | |
private function resolvePath($filepath) { | |
if ($filepath !== '' && $filepath[0] === '/') { | |
$filepath = substr($filepath, 1); | |
} | |
if ($this->path !== '') { | |
if ($filepath !== '') { | |
$filepath = $this->path . '/' . $filepath; | |
} | |
else { | |
$filepath = $this->path; | |
} | |
} | |
return $filepath; | |
} | |
/** | |
* Returns a given path as relative path to the site directory. | |
* | |
* Use this function instead of appending strings to the site path manually, | |
* because the site directory may be the root directory and thus the resulting | |
* path would be an absolute filesystem path. | |
* | |
* @param string $filepath | |
* (optional) A relative filepath to append to the site path. | |
* | |
* @return string | |
* The given $filepath, potentially prefixed with the site path. | |
* | |
* @see \Drupal\Core\Utility\Site::getAbsolutePath() | |
*/ | |
public static function getPath($filepath = '') { | |
return self::$instance->resolvePath($filepath); | |
} | |
/** | |
* Returns a given path as absolute path in the site directory. | |
* | |
* @param string $filepath | |
* (optional) A relative filepath to append to the site path. | |
* | |
* @return string | |
* The given $filepath, potentially prefixed with the site path, as an | |
* absolute filesystem path. | |
* | |
* @see \Drupal\Core\Utility\Site::getPath() | |
*/ | |
public static function getAbsolutePath($filepath = '') { | |
$filepath = self::$instance->resolvePath($filepath); | |
if ($filepath !== '') { | |
return self::$instance->root . '/' . $filepath; | |
} | |
else { | |
return self::$instance->root; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment