diff options
Diffstat (limited to 'phpBB/includes')
| -rw-r--r-- | phpBB/includes/class_loader.php | 28 | ||||
| -rw-r--r-- | phpBB/includes/constants.php | 1 | ||||
| -rw-r--r-- | phpBB/includes/extension/base.php | 35 | ||||
| -rw-r--r-- | phpBB/includes/extension/finder.php | 246 | ||||
| -rw-r--r-- | phpBB/includes/extension/interface.php | 27 | ||||
| -rw-r--r-- | phpBB/includes/extension/manager.php | 301 |
6 files changed, 630 insertions, 8 deletions
diff --git a/phpBB/includes/class_loader.php b/phpBB/includes/class_loader.php index a28d745983..bcf1ba1650 100644 --- a/phpBB/includes/class_loader.php +++ b/phpBB/includes/class_loader.php @@ -31,22 +31,25 @@ if (!defined('IN_PHPBB')) */ class phpbb_class_loader { - private $phpbb_root_path; + private $core_path; + private $ext_path; private $php_ext; private $cache; private $cached_paths = array(); /** * Creates a new phpbb_class_loader, which loads files with the given - * file extension from the given phpbb root path. + * file extension from the given core or extension path. * - * @param string $phpbb_root_path phpBB's root directory containing includes/ - * @param string $php_ext The file extension for PHP files + * @param string $core_path phpBB's include directory for core files + * @param string $ext_path phpBB's extension directory + * @param string $php_ext The file extension for PHP files * @param phpbb_cache_driver_interface $cache An implementation of the phpBB cache interface. */ - public function __construct($phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null) + public function __construct($core_path, $ext_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null) { - $this->phpbb_root_path = $phpbb_root_path; + $this->core_path = $core_path; + $this->ext_path = $ext_path; $this->php_ext = $php_ext; $this->set_cache($cache); @@ -100,7 +103,16 @@ class phpbb_class_loader */ public function resolve_path($class) { - $path_prefix = $this->phpbb_root_path . 'includes/'; + if (substr($class, 6, 4) === 'ext_') + { + $path_prefix = $this->ext_path; + $prefix_length = 10; + } + else + { + $path_prefix = $this->core_path; + $prefix_length = 6; + } if (isset($this->cached_paths[$class])) { @@ -112,7 +124,7 @@ class phpbb_class_loader return false; } - $parts = explode('_', substr($class, 6)); + $parts = explode('_', substr($class, $prefix_length)); $dirs = ''; diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php index 8ef1a4655d..d5b398b7bf 100644 --- a/phpBB/includes/constants.php +++ b/phpBB/includes/constants.php @@ -226,6 +226,7 @@ define('CONFIG_TABLE', $table_prefix . 'config'); define('CONFIRM_TABLE', $table_prefix . 'confirm'); define('DISALLOW_TABLE', $table_prefix . 'disallow'); define('DRAFTS_TABLE', $table_prefix . 'drafts'); +define('EXT_TABLE', $table_prefix . 'ext'); define('EXTENSIONS_TABLE', $table_prefix . 'extensions'); define('EXTENSION_GROUPS_TABLE', $table_prefix . 'extension_groups'); define('FORUMS_TABLE', $table_prefix . 'forums'); diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php new file mode 100644 index 0000000000..0e6c89491d --- /dev/null +++ b/phpBB/includes/extension/base.php @@ -0,0 +1,35 @@ +<?php +/** +* +* @package extension +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* +* @package extension +*/ +class phpbb_extension_base implements phpbb_extension_interface +{ + public function enable() + { + } + + public function disable() + { + } + + public function purge() + { + } +} diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php new file mode 100644 index 0000000000..fb532e52f8 --- /dev/null +++ b/phpBB/includes/extension/finder.php @@ -0,0 +1,246 @@ +<?php +/** +* +* @package extension +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* The extension finder provides a simple way to locate files in active extensions +* +* @package extension +*/ +class phpbb_extension_finder +{ + protected $extension_manager; + protected $phpbb_root_path; + protected $cache; + protected $phpEx; + + protected $query; + protected $cached_queries; + + /** + * Creates a new finder instance with its dependencies + * + * @param phpbb_extension_manager $extension_manager An extension manager + * instance that provides the finder with a list of active + * extensions and their locations + * @param string $phpbb_root_path Path to the phpbb root directory + * @param phpbb_cache_driver_interface $cache A cache instance or null + * @param string $phpEx php file extension + */ + public function __construct(phpbb_extension_manager $extension_manager, $phpbb_root_path = '', phpbb_cache_driver_interface $cache = null, $phpEx = '.php') + { + $this->extension_manager = $extension_manager; + $this->phpbb_root_path = $phpbb_root_path; + $this->cache = $cache; + $this->phpEx = $phpEx; + + $this->query = array( + 'default_path' => false, + 'default_suffix' => false, + 'default_directory' => false, + 'suffix' => false, + 'directory' => false, + ); + + $this->cached_queries = ($this->cache) ? $this->cache->get('_extension_finder') : false; + } + + /** + * Sets a default path to be searched in addition to extensions + * + * @param string $default_path The path relative to / + * @return phpbb_extension_finder This object for chaining calls + */ + public function default_path($default_path) + { + $this->query['default_path'] = $default_path; + return $this; + } + + /** + * Sets a suffix all files found in extensions must match + * + * Automatically sets the default_suffix if its value does not differ from + * the current suffix. + * + * @param string $default_path A filename suffix + * @return phpbb_extension_finder This object for chaining calls + */ + public function suffix($suffix) + { + if ($this->query['default_suffix'] === $this->query['suffix']) + { + $this->query['default_suffix'] = $suffix; + } + + $this->query['suffix'] = $suffix; + return $this; + } + + /** + * Sets a suffix all files found in the default path must match + * + * @param string $default_suffix A filename suffix + * @return phpbb_extension_finder This object for chaining calls + */ + public function default_suffix($default_suffix) + { + $this->query['default_suffix'] = $default_suffix; + return $this; + } + + /** + * Sets a directory all files found in extensions must be contained in + * + * Automatically sets the default_directory if its value does not differ from + * the current directory. + * + * @param string $directory + * @return phpbb_extension_finder This object for chaining calls + */ + public function directory($directory) + { + if (strlen($directory) > 1 && $directory[strlen($directory) - 1] === '/') + { + $directory = substr($directory, 0, -1); + } + + if ($this->query['default_directory'] === $this->query['directory']) + { + $this->query['default_directory'] = $directory; + } + + $this->query['directory'] = $directory; + return $this; + } + + /** + * Sets a directory all files found in the default path must be contained in + * + * @param string $default_directory + * @return phpbb_extension_finder This object for chaining calls + */ + public function default_directory($default_directory) + { + if (strlen($default_directory) > 1 && $default_directory[strlen($default_directory) - 1] === '/') + { + $default_directory = substr($default_directory, 0, -1); + } + + $this->query['default_directory'] = $default_directory; + return $this; + } + + /** + * Finds auto loadable php classes matching the configured options. + * + * The php file extension is automatically added to suffixes. + * + * @param bool $cache Whether the result should be cached + * @return array An array of found class names + */ + public function get_classes($cache = true) + { + $this->query['suffix'] .= $this->phpEx; + $this->query['default_suffix'] .= $this->phpEx; + + $files = $this->get_files($cache); + + $classes = array(); + foreach ($files as $file) + { + $file = preg_replace('#^includes/#', '', $file); + + $classes[] = 'phpbb_' . str_replace('/', '_', substr($file, 0, -strlen($this->phpEx))); + } + return $classes; + } + + /** + * Finds all files matching the configured options. + * + * @param bool $cache Whether the result should be cached + * @return array An array of found class names + */ + public function get_files($cache = true) + { + $query = md5(serialize($this->query)); + + if ($cache && isset($this->cached_queries[$query])) + { + return $this->cached_queries[$query]; + } + + $files = array(); + + $extensions = $this->extension_manager->all_enabled(); + + if ($this->query['default_path']) + { + $extensions['/'] = $this->phpbb_root_path . $this->query['default_path']; + } + + foreach ($extensions as $name => $path) + { + if (!file_exists($path)) + { + continue; + } + + if ($name === '/') + { + $prefix = $this->query['default_path']; + $name = ''; + $suffix = $this->query['default_suffix']; + $directory = $this->query['default_directory']; + } + else + { + $prefix = 'ext/'; + $name .= '/'; + $suffix = $this->query['suffix']; + $directory = $this->query['directory']; + } + + // match only first directory if leading slash is given + $directory_pattern = ($directory && $directory[0] === '/') ? '#^' : '#' . DIRECTORY_SEPARATOR; + $directory_pattern .= preg_quote($directory . DIRECTORY_SEPARATOR, '#') . '#'; + + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)); + foreach ($iterator as $file_info) + { + if (!$file_info->isDir()) + { + $relative_path = $iterator->getInnerIterator()->getSubPathname(); + + if ((!$suffix || substr($relative_path, -strlen($suffix)) == $suffix) && + (!$directory || preg_match($directory_pattern, DIRECTORY_SEPARATOR . $relative_path))) + { + $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $prefix . $name . $relative_path); + } + } + } + } + + if ($cache && $this->cache) + { + $this->cached_queries[$query] = $files; + $this->cache->put('_extension_finder', $this->cached_queries); + } + + return $files; + } +} diff --git a/phpBB/includes/extension/interface.php b/phpBB/includes/extension/interface.php new file mode 100644 index 0000000000..40a5a066a3 --- /dev/null +++ b/phpBB/includes/extension/interface.php @@ -0,0 +1,27 @@ +<?php +/** +* +* @package extension +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* +* @package extension +*/ +interface phpbb_extension_interface +{ + public function enable(); + public function disable(); + public function purge(); +} diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php new file mode 100644 index 0000000000..736c706e77 --- /dev/null +++ b/phpBB/includes/extension/manager.php @@ -0,0 +1,301 @@ +<?php +/** +* +* @package extension +* @copyright (c) 2011 phpBB Group +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* +*/ + +/** +* @ignore +*/ +if (!defined('IN_PHPBB')) +{ + exit; +} + +/** +* The extension manager provides means to activate/deactivate extensions. +* +* @package extension +*/ +class phpbb_extension_manager +{ + protected $cache; + protected $phpEx; + protected $extensions; + protected $extension_table; + protected $phpbb_root_path; + + /** + * Creates a manager and loads information from database + * + * @param dbal $db A database connection + * @param string $extension_table The name of the table holding extensions + * @param string $phpbb_root_path Path to the phpbb includes directory. + * @param string $phpEx php file extension + * @param phpbb_cache_driver_interface $cache A cache instance or null + */ + public function __construct(dbal $db, $extension_table, $phpbb_root_path, $phpEx = '.php', phpbb_cache_driver_interface $cache = null) + { + $this->phpbb_root_path = $phpbb_root_path; + $this->db = $db; + $this->cache = $cache; + $this->phpEx = $phpEx; + $this->extension_table = $extension_table; + + if (false === ($this->extensions = $this->cache->get('_extensions'))) + { + $this->load_extensions(); + } + } + + /** + * Loads all extension information from the database + * + * @return null + */ + protected function load_extensions() + { + $sql = 'SELECT * + FROM ' . $this->extension_table; + + $result = $this->db->sql_query($sql); + $extensions = $this->db->sql_fetchrowset($result); + + $this->extensions = array(); + foreach ($extensions as $extension) + { + $extension['ext_path'] = $this->get_extension_path($extension['ext_name']); + $this->extensions[$extension['ext_name']] = $extension; + } + + ksort($this->extensions); + $this->cache->put('_extensions', $this->extensions); + } + + /** + * Generates the path to an extension + * + * @param string $name The name of the extension + * @return string Path to an extension + */ + public function get_extension_path($name) + { + return $this->phpbb_root_path . 'ext/' . basename($name) . '/'; + } + + /** + * Instantiates the extension meta class for the given name. + * + * @param string $name The extension name + * @return phpbb_extension_interface Instance of the extension meta class or + * phpbb_extension_base if the class does not exist + */ + public function get_extension($name) + { + $extension_class_name = 'phpbb_ext_' . $name; + + if (class_exists($extension_class_name)) + { + return new $extension_class_name; + } + else + { + return new phpbb_extension_base; + } + } + + /** + * Enables an extension + * + * Calls the enable method on the extension's meta class to allow it to + * make database changes and execute other initialisation code. + * + * @param string $name The extension's name + * @return null + */ + public function enable($name) + { + // ignore extensions that are already enabled + if (isset($this->extensions[$name]) && $this->extensions[$name]['ext_active']) + { + return; + } + + $extension = $this->get_extension($name); + $extension->enable(); + + $extension_data = array( + 'ext_name' => $name, + 'ext_active' => true, + ); + + $this->extensions[$name] = $extension_data; + $this->extensions[$name]['ext_path'] = $this->get_extension_path($extension_data['ext_name']); + ksort($this->extensions); + + $sql = 'UPDATE ' . $this->extension_table . ' + SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . " + WHERE ext_name = '" . $this->db->sql_escape($name) . "'"; + $this->db->sql_query($sql); + + if (!$this->db->sql_affectedrows()) + { + $sql = 'INSERT INTO ' . $this->extension_table . ' + ' . $this->db->sql_build_array('INSERT', $extension_data); + $this->db->sql_query($sql); + } + } + + /** + * Disables an extension + * + * Calls the disable method on the extension's meta class to allow it to + * process the event. + * + * @param string $name The extension's name + * @return null + */ + public function disable($name) + { + // ignore extensions that are already disabled + if (!isset($this->extensions[$name]) || !$this->extensions[$name]['ext_active']) + { + return; + } + + $extension = $this->get_extension($name); + $extension->disable(); + + $extension_data = array( + 'ext_active' => false, + ); + $this->extensions[$name]['ext_active'] = false; + ksort($this->extensions); + + $sql = 'UPDATE ' . $this->extension_table . ' + SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . " + WHERE ext_name = '" . $this->db->sql_escape($name) . "'"; + $this->db->sql_query($sql); + } + + /** + * Purge an extension + * + * Disables the extension first if active, and then calls purge on the + * extension's meta class to delete the extension's database content. + * + * @param string $name The extension's name + * @return null + */ + public function purge($name) + { + // ignore extensions that do not exist + if (!isset($this->extensions[$name])) + { + return; + } + + // disable first if necessary + if ($this->extensions[$name]['ext_active']) + { + $this->disable($name); + } + + $extension = $this->get_extension($name); + $extension->purge(); + + unset($this->extensions[$name]); + + $sql = 'DELETE FROM ' . $this->extension_table . " + WHERE ext_name = '" . $this->db->sql_escape($name) . "'"; + $this->db->sql_query($sql); + } + + /** + * Retrieves a list of all available extensions on the filesystem + * + * @return array An array with extension names as keys and paths to the + * extension as values + */ + public function all_available() + { + $available = array(); + + $iterator = new DirectoryIterator($this->phpbb_root_path . 'ext/'); + foreach ($iterator as $file_info) + { + $path = $this->phpbb_root_path . 'ext/' . $file_info->getBasename() . '/'; + if (!$file_info->isDot() && $file_info->isDir() && file_exists($path)) + { + $available[$file_info->getBasename()] = $path; + } + } + ksort($available); + return $available; + } + + /** + * Retrieves all configured extensions. + * + * All enabled and disabled extensions are considered configured. A purged + * extension that is no longer in the database is not configured. + * + * @return array An array with extension names as keys and and the + * database stored extension information as values + */ + public function all_configured() + { + return $this->extensions; + } + + /** + * Retrieves all enabled extensions. + * + * @return array An array with extension names as keys and and the + * database stored extension information as values + */ + public function all_enabled() + { + $enabled = array(); + foreach ($this->extensions as $name => $data) + { + if ($data['ext_active']) + { + $enabled[$name] = $data['ext_path']; + } + } + return $enabled; + } + + /** + * Retrieves all disabled extensions. + * + * @return array An array with extension names as keys and and the + * database stored extension information as values + */ + public function all_disabled() + { + $disabled = array(); + foreach ($this->extensions as $name => $data) + { + if (!$data['ext_active']) + { + $disabled[$name] = $data['ext_path']; + } + } + return $disabled; + } + + /** + * Instantiates a phpbb_extension_finder. + * + * @return phpbb_extension_finder An extension finder instance + */ + public function get_finder() + { + return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->phpEx); + } +} |
