diff --git a/README.txt b/README.txt index 64d5960..8827d3c 100644 --- a/README.txt +++ b/README.txt @@ -14,7 +14,7 @@ queries and DB migrations, and misc utilities like run cron or clear cache. REQUIREMENTS ============ * To use Drush from the command line, you'll need a - CLI-mode capable PHP binary version 5.2 or greater. + CLI-mode capable PHP binary version 5.3 or greater. * Drush commands that work with git require git 1.7 or greater. diff --git a/drush.php b/drush.php index 734106d..28f56db 100755 --- a/drush.php +++ b/drush.php @@ -5,7 +5,7 @@ * @file * drush is a PHP script implementing a command line shell for Drupal. * - * @requires PHP CLI 5.2.0, or newer. + * @requires PHP CLI 5.3.0, or newer. */ require(dirname(__FILE__) . '/includes/bootstrap.inc'); diff --git a/includes/DrushAutoLoader.php b/includes/DrushAutoLoader.php new file mode 100644 index 0000000..4713a94 --- /dev/null +++ b/includes/DrushAutoLoader.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * @api + */ +class DrushAutoLoader { + private $namespaces = array(); + private $prefixes = array(); + private $namespaceFallbacks = array(); + private $prefixFallbacks = array(); + private $cache_prefix; + + private static $apc = NULL; + + + /** + * Constructor. + * + * @param string $prefix A prefix to create a namespace in APC + * + * @api + */ + public function __construct($cache_prefix = '') { + if (!isset(self::$apc)) { + self::$apc = extension_loaded('apc'); + } + $this->prefix = $cache_prefix; + } + + /** + * Gets the configured namespaces. + * + * @return array A hash with namespaces as keys and directories as values + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * Gets the configured class prefixes. + * + * @return array A hash with class prefixes as keys and directories as values + */ + public function getPrefixes() + { + return $this->prefixes; + } + + /** + * Gets the directory(ies) to use as a fallback for namespaces. + * + * @return array An array of directories + */ + public function getNamespaceFallbacks() + { + return $this->namespaceFallbacks; + } + + /** + * Gets the directory(ies) to use as a fallback for class prefixes. + * + * @return array An array of directories + */ + public function getPrefixFallbacks() + { + return $this->prefixFallbacks; + } + + /** + * Registers the directory to use as a fallback for namespaces. + * + * @param array $dirs An array of directories + * + * @api + */ + public function registerNamespaceFallbacks(array $dirs) + { + $this->namespaceFallbacks = $dirs; + } + + /** + * Registers the directory to use as a fallback for class prefixes. + * + * @param array $dirs An array of directories + * + * @api + */ + public function registerPrefixFallbacks(array $dirs) + { + $this->prefixFallbacks = $dirs; + } + + /** + * Registers an array of namespaces + * + * @param array $namespaces An array of namespaces (namespaces as keys and locations as values) + * + * @api + */ + public function registerNamespaces(array $namespaces) + { + foreach ($namespaces as $namespace => $locations) { + $this->namespaces[$namespace] = (array) $locations; + } + } + + /** + * Registers a namespace. + * + * @param string $namespace The namespace + * @param array|string $paths The location(s) of the namespace + * + * @api + */ + public function registerNamespace($namespace, $paths) { + $this->namespaces[$namespace] = (array)$paths; + } + + /** + * Registers an array of classes using the PEAR naming convention. + * + * @param array $classes An array of classes (prefixes as keys and locations as values) + * + * @api + */ + public function registerPrefixes(array $classes) + { + foreach ($classes as $prefix => $locations) { + $this->prefixes[$prefix] = (array) $locations; + } + } + + /** + * Registers a set of classes using the PEAR naming convention. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + * + * @api + */ + public function registerPrefix($prefix, $paths) + { + $this->prefixes[$prefix] = (array) $paths; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not + * + * @api + */ + public function register($prepend = false) { + if (!isset(self::$apc)) { + self::$apc = extension_loaded('apc'); + } + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + */ + public function loadClass($class) { + if ($file = $this->findFileCached($class)) { + require $file; + } + } + + /** + * Finds a file by class name while caching lookups to APC. + * + * @param string $class A class name to resolve to file + */ + public function findFileCached($class) { + if (self::$apc) { + if (FALSE === $file = apc_fetch($this->prefix.$class)) { + apc_store($this->prefix.$class, $file = $this->findFile($class)); + } + } + else { + $file = $this->findFile($class); + } + + return $file; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) { + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $namespace = substr($class, 0, $pos); + foreach ($this->namespaces as $ns => $dirs) { + foreach ($dirs as $dir) { + if (0 === strpos($namespace, $ns)) { + $className = substr($class, $pos + 1); + $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + } + + foreach ($this->namespaceFallbacks as $dir) { + $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + else { + // PEAR-like class name + foreach ($this->prefixes as $prefix => $dirs) { + foreach ($dirs as $dir) { + if (0 === strpos($class, $prefix)) { + $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + } + + foreach ($this->prefixFallbacks as $dir) { + $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + } +} + diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 53f98ff..c2ca6aa 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -935,6 +935,14 @@ function drush_bootstrap_value($context, $value = null) { function drush_bootstrap_prepare() { define('DRUSH_BASE_PATH', dirname(__FILE__) . '/..'); + require_once DRUSH_BASE_PATH . '/includes/DrushAutoLoader.php'; + // Instantiate PSR-0 autoloader. + $loader = new DrushAutoLoader(); + // Register the Drush namespace. + $loader->registerNamespace('Drush', DRUSH_BASE_PATH . '/lib'); + // Activate the autoloader. + $loader->register(); + require_once DRUSH_BASE_PATH . '/includes/environment.inc'; require_once DRUSH_BASE_PATH . '/includes/command.inc'; require_once DRUSH_BASE_PATH . '/includes/drush.inc'; @@ -955,7 +963,7 @@ function drush_bootstrap_prepare() { } // Check supported version of PHP. - define('DRUSH_MINIMUM_PHP', '5.2.0'); + define('DRUSH_MINIMUM_PHP', '5.3.0'); if (version_compare(phpversion(), DRUSH_MINIMUM_PHP) < 0) { die('Your command line PHP installation is too old. Drush requires at least PHP ' . DRUSH_MINIMUM_PHP . "\n"); } diff --git a/includes/cache.inc b/includes/cache.inc index 415e59b..ab26c8b 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -192,244 +192,3 @@ function drush_get_cid($prefix, $contexts = array(), $params = array()) { return DRUSH_VERSION . '-' . $prefix . '-' . md5(implode("", $cid)); } - -/** - * Interface for cache implementations. - * - * All cache implementations have to implement this interface. - * DrushDatabaseCache provides the default implementation, which can be - * consulted as an example. - * - * To make Drush use your implementation for a certain cache bin, you have to - * set a variable with the name of the cache bin as its key and the name of - * your class as its value. For example, if your implementation of - * DrushCacheInterface was called MyCustomCache, the following line in - * drushrc.php would make Drush use it for the 'example' bin: - * @code - * $options['cache-class-example'] = 'MyCustomCache; - * @endcode - * - * Additionally, you can register your cache implementation to be used by - * default for all cache bins by setting the option 'cache-default-class' to - * the name of your implementation of the DrushCacheInterface, e.g. - * @code - * $options['cache-default-class'] = 'MyCustomCache; - * @endcode - * - * @see _drush_cache_get_object() - * @see DrupalCacheInterface - */ -interface DrushCacheInterface { - /** - * Constructor. - * - * @param $bin - * The cache bin for which the object is created. - */ - function __construct($bin); - - /** - * Return data from the persistent cache. - * - * @param $cid - * The cache ID of the data to retrieve. - * @return - * The cache or FALSE on failure. - */ - function get($cid); - - /** - * Return data from the persistent cache when given an array of cache IDs. - * - * @param $cids - * An array of cache IDs for the data to retrieve. This is passed by - * reference, and will have the IDs successfully returned from cache - * removed. - * @return - * An array of the items successfully returned from cache indexed by cid. - */ - function getMultiple(&$cids); - - /** - * Store data in the persistent cache. - * - * @param $cid - * The cache ID of the data to store. - * @param $data - * The data to store in the cache. - * @param $expire - * One of the following values: - * - DRUSH_CACHE_PERMANENT: Indicates that the item should never be removed unless - * explicitly told to using _drush_cache_clear_all() with a cache ID. - * - DRUSH_CACHE_TEMPORARY: Indicates that the item should be removed at the next - * general cache wipe. - * - A Unix timestamp: Indicates that the item should be kept at least until - * the given time, after which it behaves like CACHE_TEMPORARY. - */ - function set($cid, $data, $expire = DRUSH_CACHE_PERMANENT); - - /** - * Expire data from the cache. If called without arguments, expirable - * entries will be cleared from all known cache bins. - * - * @param $cid - * If set, the cache ID to delete. Otherwise, all cache entries that can - * expire are deleted. - * @param $wildcard - * If set to TRUE, the $cid is treated as a substring - * to match rather than a complete ID. The match is a right hand - * match. If '*' is given as $cid, the bin $bin will be emptied. - */ - function clear($cid = NULL, $wildcard = FALSE); - - /** - * Check if a cache bin is empty. - * - * A cache bin is considered empty if it does not contain any valid data for - * any cache ID. - * - * @return - * TRUE if the cache bin specified is empty. - */ - function isEmpty(); -} - -/** - * Default cache implementation. - * - * This is Drush's default cache implementation. It uses plain text files - * containing serialized php to store cached data. Each cache bin corresponds - * to a directory by the same name. - */ -class DrushFileCache implements DrushCacheInterface { - const EXTENSION = '.cache'; - protected $bin; - - function __construct($bin) { - $this->bin = $bin; - $this->directory = $this->cacheDirectory(); - } - - function cacheDirectory($bin = NULL) { - $bin = $bin ? '/' . $bin : '/' . $this->bin; - $cache_dir = drush_directory_cache() . '/' . $bin; - return drush_mkdir($cache_dir) ? $cache_dir : FALSE; - } - - function get($cid) { - $cids = array($cid); - $cache = $this->getMultiple($cids); - return reset($cache); - } - - function getMultiple(&$cids) { - try { - $cache = array(); - foreach ($cids as $cid) { - $filename = $this->getFilePath($cid); - if (!file_exists($filename)) throw new Exception; - - $item = $this->readFile($filename); - if ($item) { - $cache[$cid] = $item; - } - } - $cids = array_diff($cids, array_keys($cache)); - return $cache; - } - catch (Exception $e) { - return array(); - } - } - - function readFile($filename) { - $item = file_get_contents($filename); - return $item ? unserialize($item) : FALSE; - } - - function set($cid, $data, $expire = DRUSH_CACHE_PERMANENT, array $headers = NULL) { - $created = time(); - - $cache = new stdClass; - $cache->cid = $cid; - $cache->data = is_object($data) ? clone $data : $data; - $cache->created = $created; - $cache->headers = $headers; - if ($expire == DRUSH_CACHE_TEMPORARY) { - $cache->expire = $created + 2591999; - } - // Expire time is in seconds if less than 30 days, otherwise is a timestamp. - elseif ($expire != DRUSH_CACHE_PERMANENT && $expire < 2592000) { - $cache->expire = $created + $expire; - } - else { - $cache->expire = $expire; - } - - $filename = $this->getFilePath($cid); - return $this->writeFile($filename, $cache); - } - - function writeFile($filename, $cache) { - return file_put_contents($filename, serialize($cache)); - } - - function clear($cid = NULL, $wildcard = FALSE) { - $bin_dir = $this->cacheDirectory(); - $files = array(); - if (empty($cid)) { - drush_delete_dir($bin_dir, TRUE); - } - else { - if ($wildcard) { - if ($cid == '*') { - drush_delete_dir($bin_dir, TRUE); - } - else { - $matches = drush_scan_directory($bin_dir, "/^$cid/", array('.', '..')); - $files = $files + array_keys($matches); - } - } - else { - $files[] = $this->getFilePath($cid); - } - - foreach ($files as $f) { - drush_register_file_for_deletion($f); - } - } - } - - function isEmpty() { - $files = drush_scan_directory($dir, "//", array('.', '..')); - return empty($files); - } - - /** - * Converts a cache id to a full path. - * - * @param $cid - * The cache ID of the data to retrieve. - * @return - * The full path to the cache file. - */ - protected function getFilePath($cid) { - return $this->directory . '/' . str_replace(array(':'), '.', $cid) . self::EXTENSION; - } -} - -/** - * JSON cache storage backend. - */ -class DrushJSONCache extends DrushFileCache { - const EXTENSION = '.json'; - - function readFile($filename) { - $item = file_get_contents($filename); - return $item ? (object)drush_json_decode($item) : FALSE; - } - - function writeFile($filename, $cache) { - return file_put_contents($filename, drush_json_encode($cache)); - } -} diff --git a/includes/drush.inc b/includes/drush.inc index 1d3ecb4..2e2e088 100644 --- a/includes/drush.inc +++ b/includes/drush.inc @@ -885,7 +885,7 @@ function drush_tarball_extract($path, $destination = FALSE, $listing = FALSE) { } /** - * Extract a tarball to the lib directory. + * Extract a tarball to the vendor directory. * Checks for reported success, but callers should normally check for existence * of specific expected file(s) in the library. * @@ -895,15 +895,15 @@ function drush_tarball_extract($path, $destination = FALSE, $listing = FALSE) { * @return string * TRUE is the download and extraction reported success, FALSE otherwise. */ -function drush_lib_fetch($url) { - $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib'); - if (!is_writable($lib)) { - return drush_set_error('DRUSH_LIB_UNWRITABLE', dt("Drush needs to download a library from !url in order to function, and the attempt to download this file automatically failed because you do not have permission to write to the library directory !path. To continue you will need to manually download the package from !url, extract it, and copy the directory into your !path directory.", array('!path' => $lib, '!url' => $url))); +function drush_vendor_fetch($url) { + $vendor = drush_get_option('vendor', DRUSH_BASE_PATH . '/vendor'); + if (!is_writable($vendor)) { + return drush_set_error('DRUSH_VENDOR_UNWRITABLE', dt("Drush needs to download a library from !url in order to function, and the attempt to download this file automatically failed because you do not have permission to write to the library directory !path. To continue you will need to manually download the package from !url, extract it, and copy the directory into your !path directory.", array('!path' => $lib, '!url' => $url))); } // We use an arbitary filename, since some sources (e.g. github) do not // include a filename in the URL. - $path = $lib . '/drush-library-' . mt_rand() . '.tar.gz'; + $path = $vendor. '/drush-library-' . mt_rand() . '.tar.gz'; return drush_download_file($url, $path) && drush_tarball_extract($path); } diff --git a/includes/filesystem.inc b/includes/filesystem.inc index ede7ea3..30af7f9 100644 --- a/includes/filesystem.inc +++ b/includes/filesystem.inc @@ -306,11 +306,7 @@ function drush_find_tmp() { else { $directories[] = '/tmp'; } - // This function exists in PHP 5 >= 5.2.1, but drush - // requires PHP 5 >= 5.2.0, so we check for it. - if (function_exists('sys_get_temp_dir')) { - $directories[] = sys_get_temp_dir(); - } + $directories[] = sys_get_temp_dir(); foreach ($directories as $directory) { if (is_dir($directory) && is_writable($directory)) { diff --git a/vendor/README.txt b/vendor/README.txt new file mode 100644 index 0000000..9f32e97 --- /dev/null +++ b/vendor/README.txt @@ -0,0 +1 @@ +Vendor directory used to store downloaded external libraries.