From 14f1e581faa3b66e7689c55c1e9c0485c0872b1e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 9 Jun 2011 05:13:26 +0200 Subject: [PATCH 01/67] [feature/extension-manager] Extension Manager & Finder Extensions RFC: http://area51.phpbb.com/phpBB/viewtopic.php?f=84&t=41499 Ticket: http://tracker.phpbb.com/browse/PHPBB3-10323 PHPBB3-10323 --- phpBB/common.php | 2 +- phpBB/develop/create_schema_files.php | 11 + phpBB/download/file.php | 2 +- phpBB/includes/class_loader.php | 28 +- phpBB/includes/constants.php | 1 + phpBB/includes/extension/base.php | 35 ++ phpBB/includes/extension/finder.php | 246 ++++++++++++++ phpBB/includes/extension/interface.php | 27 ++ phpBB/includes/extension/manager.php | 301 ++++++++++++++++++ phpBB/install/database_update.php | 18 +- phpBB/install/index.php | 2 +- phpBB/install/schemas/firebird_schema.sql | 9 + phpBB/install/schemas/mssql_schema.sql | 16 + phpBB/install/schemas/mysql_40_schema.sql | 9 + phpBB/install/schemas/mysql_41_schema.sql | 9 + phpBB/install/schemas/oracle_schema.sql | 13 + phpBB/install/schemas/postgres_schema.sql | 11 + phpBB/install/schemas/sqlite_schema.sql | 9 + tests/bootstrap.php | 2 +- tests/class_loader/class_loader_test.php | 9 +- tests/class_loader/ext/foo/class.php | 6 + tests/extension/ext/bar/my/hidden_class.php | 5 + tests/extension/ext/foo/a_class.php | 5 + tests/extension/ext/foo/b_class.php | 5 + tests/extension/ext/foo/foo.php | 5 + .../ext/foo/sub/type/alternative.php | 5 + tests/extension/ext/foo/type/alternative.php | 5 + tests/extension/ext/foo/typewrong/error.php | 5 + tests/extension/ext/moo/feature_class.php | 5 + tests/extension/finder_test.php | 133 ++++++++ tests/extension/fixtures/extensions.xml | 15 + .../includes/default/implementation.php | 5 + tests/extension/manager_test.php | 89 ++++++ tests/mock/extension_manager.php | 16 + 34 files changed, 1049 insertions(+), 15 deletions(-) create mode 100644 phpBB/includes/extension/base.php create mode 100644 phpBB/includes/extension/finder.php create mode 100644 phpBB/includes/extension/interface.php create mode 100644 phpBB/includes/extension/manager.php create mode 100644 tests/class_loader/ext/foo/class.php create mode 100644 tests/extension/ext/bar/my/hidden_class.php create mode 100644 tests/extension/ext/foo/a_class.php create mode 100644 tests/extension/ext/foo/b_class.php create mode 100644 tests/extension/ext/foo/foo.php create mode 100644 tests/extension/ext/foo/sub/type/alternative.php create mode 100644 tests/extension/ext/foo/type/alternative.php create mode 100644 tests/extension/ext/foo/typewrong/error.php create mode 100644 tests/extension/ext/moo/feature_class.php create mode 100644 tests/extension/finder_test.php create mode 100644 tests/extension/fixtures/extensions.xml create mode 100644 tests/extension/includes/default/implementation.php create mode 100644 tests/extension/manager_test.php create mode 100644 tests/mock/extension_manager.php diff --git a/phpBB/common.php b/phpBB/common.php index 524c05ae70..7ff06e68d7 100644 --- a/phpBB/common.php +++ b/phpBB/common.php @@ -96,7 +96,7 @@ require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler'); // Setup class loader first -$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx); +$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); $class_loader->register(); // set up caching diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index 2057d292b7..9f2015f38e 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -1031,6 +1031,17 @@ function get_schema_struct() ), ); + $schema_data['phpbb_ext'] = array( + 'COLUMNS' => array( + 'ext_name' => array('VCHAR', ''), + 'ext_active' => array('BOOL', 0), + ), + 'KEYS' => array( + 'ext_name' => array('UNIQUE', 'ext_name'), + 'ext_active' => array('INDEX', 'ext_active'), + ), + ); + $schema_data['phpbb_extensions'] = array( 'COLUMNS' => array( 'extension_id' => array('UINT', NULL, 'auto_increment'), diff --git a/phpBB/download/file.php b/phpBB/download/file.php index ec15d36e08..956af75c81 100644 --- a/phpBB/download/file.php +++ b/phpBB/download/file.php @@ -46,7 +46,7 @@ if (isset($_GET['avatar'])) require($phpbb_root_path . 'includes/functions_download' . '.' . $phpEx); require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); - $class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx); + $class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); $class_loader->register(); // set up caching 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 @@ +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 @@ +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); + } +} diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 18741191d8..b2419df3c9 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -104,8 +104,12 @@ if (!defined('LOGIN_ATTEMPT_TABLE')) { define('LOGIN_ATTEMPT_TABLE', $table_prefix . 'login_attempts'); } +if (!defined('EXT_TABLE')) +{ + define('EXT_TABLE', $table_prefix . 'ext'); +} -$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx); +$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); $class_loader->register(); // set up caching @@ -1048,6 +1052,18 @@ function database_update_info() // Changes from 3.1.0-dev to 3.1.0-A1 '3.1.0-dev' => array( + 'add_tables' => array( + EXT_TABLE => array( + 'COLUMNS' => array( + 'ext_name' => array('VCHAR', ''), + 'ext_active' => array('BOOL', 0), + ), + 'KEYS' => array( + 'ext_name' => array('UNIQUE', 'ext_name'), + 'ext_active' => array('INDEX', 'ext_active'), + ), + ), + ), 'add_columns' => array( GROUPS_TABLE => array( 'group_teampage' => array('UINT', 0, 'after' => 'group_legend'), diff --git a/phpBB/install/index.php b/phpBB/install/index.php index a4ff93e701..fbe8ae63a7 100644 --- a/phpBB/install/index.php +++ b/phpBB/install/index.php @@ -82,7 +82,7 @@ include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); require($phpbb_root_path . 'includes/functions_install.' . $phpEx); -$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx); +$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); $class_loader->register(); // set up caching diff --git a/phpBB/install/schemas/firebird_schema.sql b/phpBB/install/schemas/firebird_schema.sql index 67e5547bb6..5f5aaaaa45 100644 --- a/phpBB/install/schemas/firebird_schema.sql +++ b/phpBB/install/schemas/firebird_schema.sql @@ -281,6 +281,15 @@ BEGIN END;; +# Table: 'phpbb_ext' +CREATE TABLE phpbb_ext ( + ext_name VARCHAR(255) CHARACTER SET NONE DEFAULT '' NOT NULL, + ext_active INTEGER DEFAULT 0 NOT NULL +);; + +CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext(ext_name);; +CREATE INDEX phpbb_ext_ext_active ON phpbb_ext(ext_active);; + # Table: 'phpbb_extensions' CREATE TABLE phpbb_extensions ( extension_id INTEGER NOT NULL, diff --git a/phpBB/install/schemas/mssql_schema.sql b/phpBB/install/schemas/mssql_schema.sql index fe69670ded..052531993f 100644 --- a/phpBB/install/schemas/mssql_schema.sql +++ b/phpBB/install/schemas/mssql_schema.sql @@ -358,6 +358,22 @@ CREATE INDEX [save_time] ON [phpbb_drafts]([save_time]) ON [PRIMARY] GO +/* + Table: 'phpbb_ext' +*/ +CREATE TABLE [phpbb_ext] ( + [ext_name] [varchar] (255) DEFAULT ('') NOT NULL , + [ext_active] [int] DEFAULT (0) NOT NULL +) ON [PRIMARY] +GO + +CREATE UNIQUE INDEX [ext_name] ON [phpbb_ext]([ext_name]) ON [PRIMARY] +GO + +CREATE INDEX [ext_active] ON [phpbb_ext]([ext_active]) ON [PRIMARY] +GO + + /* Table: 'phpbb_extensions' */ diff --git a/phpBB/install/schemas/mysql_40_schema.sql b/phpBB/install/schemas/mysql_40_schema.sql index da6ce35be3..03402d7ed1 100644 --- a/phpBB/install/schemas/mysql_40_schema.sql +++ b/phpBB/install/schemas/mysql_40_schema.sql @@ -191,6 +191,15 @@ CREATE TABLE phpbb_drafts ( ); +# Table: 'phpbb_ext' +CREATE TABLE phpbb_ext ( + ext_name varbinary(255) DEFAULT '' NOT NULL, + ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL, + UNIQUE ext_name (ext_name), + KEY ext_active (ext_active) +); + + # Table: 'phpbb_extensions' CREATE TABLE phpbb_extensions ( extension_id mediumint(8) UNSIGNED NOT NULL auto_increment, diff --git a/phpBB/install/schemas/mysql_41_schema.sql b/phpBB/install/schemas/mysql_41_schema.sql index cdbe377178..8876b52a30 100644 --- a/phpBB/install/schemas/mysql_41_schema.sql +++ b/phpBB/install/schemas/mysql_41_schema.sql @@ -191,6 +191,15 @@ CREATE TABLE phpbb_drafts ( ) CHARACTER SET `utf8` COLLATE `utf8_bin`; +# Table: 'phpbb_ext' +CREATE TABLE phpbb_ext ( + ext_name varchar(255) DEFAULT '' NOT NULL, + ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL, + UNIQUE ext_name (ext_name), + KEY ext_active (ext_active) +) CHARACTER SET `utf8` COLLATE `utf8_bin`; + + # Table: 'phpbb_extensions' CREATE TABLE phpbb_extensions ( extension_id mediumint(8) UNSIGNED NOT NULL auto_increment, diff --git a/phpBB/install/schemas/oracle_schema.sql b/phpBB/install/schemas/oracle_schema.sql index 8797457e87..df31f6d3d6 100644 --- a/phpBB/install/schemas/oracle_schema.sql +++ b/phpBB/install/schemas/oracle_schema.sql @@ -407,6 +407,19 @@ END; / +/* + Table: 'phpbb_ext' +*/ +CREATE TABLE phpbb_ext ( + ext_name varchar2(255) DEFAULT '' , + ext_active number(1) DEFAULT '0' NOT NULL, + CONSTRAINT u_phpbb_ext_name UNIQUE (ext_name) +) +/ + +CREATE INDEX phpbb_ext_ext_active ON phpbb_ext (ext_active) +/ + /* Table: 'phpbb_extensions' */ diff --git a/phpBB/install/schemas/postgres_schema.sql b/phpBB/install/schemas/postgres_schema.sql index 3c79aacd6b..dbe891a3ac 100644 --- a/phpBB/install/schemas/postgres_schema.sql +++ b/phpBB/install/schemas/postgres_schema.sql @@ -312,6 +312,17 @@ CREATE TABLE phpbb_drafts ( CREATE INDEX phpbb_drafts_save_time ON phpbb_drafts (save_time); +/* + Table: 'phpbb_ext' +*/ +CREATE TABLE phpbb_ext ( + ext_name varchar(255) DEFAULT '' NOT NULL, + ext_active INT2 DEFAULT '0' NOT NULL CHECK (ext_active >= 0) +); + +CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name); +CREATE INDEX phpbb_ext_ext_active ON phpbb_ext (ext_active); + /* Table: 'phpbb_extensions' */ diff --git a/phpBB/install/schemas/sqlite_schema.sql b/phpBB/install/schemas/sqlite_schema.sql index e0631160fd..3c1d476daa 100644 --- a/phpBB/install/schemas/sqlite_schema.sql +++ b/phpBB/install/schemas/sqlite_schema.sql @@ -186,6 +186,15 @@ CREATE TABLE phpbb_drafts ( CREATE INDEX phpbb_drafts_save_time ON phpbb_drafts (save_time); +# Table: 'phpbb_ext' +CREATE TABLE phpbb_ext ( + ext_name varchar(255) NOT NULL DEFAULT '', + ext_active INTEGER UNSIGNED NOT NULL DEFAULT '0' +); + +CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name); +CREATE INDEX phpbb_ext_ext_active ON phpbb_ext (ext_active); + # Table: 'phpbb_extensions' CREATE TABLE phpbb_extensions ( extension_id INTEGER PRIMARY KEY NOT NULL , diff --git a/tests/bootstrap.php b/tests/bootstrap.php index b7c3534cde..fbe23c1835 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -32,7 +32,7 @@ else require_once $phpbb_root_path . 'includes/constants.php'; require_once $phpbb_root_path . 'includes/class_loader.' . $phpEx; -$class_loader = new phpbb_class_loader($phpbb_root_path, '.php'); +$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.php'); $class_loader->register(); require_once 'test_framework/phpbb_test_case_helpers.php'; diff --git a/tests/class_loader/class_loader_test.php b/tests/class_loader/class_loader_test.php index 0c7fe3f97a..7d5f57aac3 100644 --- a/tests/class_loader/class_loader_test.php +++ b/tests/class_loader/class_loader_test.php @@ -26,7 +26,7 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase public function test_resolve_path() { $prefix = dirname(__FILE__) . '/'; - $class_loader = new phpbb_class_loader($prefix); + $class_loader = new phpbb_class_loader($prefix . 'includes/', $prefix . 'ext/'); $prefix .= 'includes/'; @@ -56,6 +56,11 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase $class_loader->resolve_path('phpbb_dir2'), 'Class with name of dir within dir (short class name)' ); + $this->assertEquals( + dirname(__FILE__) . '/ext/foo/class.php', + $class_loader->resolve_path('phpbb_ext_foo_class'), + 'Extension class' + ); } public function test_resolve_cached() @@ -64,7 +69,7 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase $cache = new phpbb_mock_cache($cacheMap); $prefix = dirname(__FILE__) . '/'; - $class_loader = new phpbb_class_loader($prefix, '.php', $cache); + $class_loader = new phpbb_class_loader($prefix . 'includes/', $prefix . 'ext/', '.php', $cache); $prefix .= 'includes/'; diff --git a/tests/class_loader/ext/foo/class.php b/tests/class_loader/ext/foo/class.php new file mode 100644 index 0000000000..7b1555c98d --- /dev/null +++ b/tests/class_loader/ext/foo/class.php @@ -0,0 +1,6 @@ +extension_manager = new phpbb_mock_extension_manager(array( + 'foo' => array( + 'ext_name' => 'foo', + 'ext_active' => '1', + 'ext_path' => dirname(__FILE__) . '/ext/foo/', + ), + 'bar' => array( + 'ext_name' => 'bar', + 'ext_active' => '1', + 'ext_path' => dirname(__FILE__) . '/ext/bar/', + ), + )); + + $this->finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/'); + } + + public function test_suffix_get_classes() + { + $classes = $this->finder + ->default_path('includes/default/') + ->suffix('_class') + ->default_suffix('') + ->get_classes(); + + sort($classes); + $this->assertEquals( + array( + 'phpbb_default_implementation', + 'phpbb_ext_bar_my_hidden_class', + 'phpbb_ext_foo_a_class', + 'phpbb_ext_foo_b_class', + ), + $classes + ); + } + + public function test_directory_get_classes() + { + $classes = $this->finder + ->default_path('includes/default/') + ->directory('type') + ->default_directory('') + ->get_classes(); + + sort($classes); + $this->assertEquals( + array( + 'phpbb_default_implementation', + 'phpbb_ext_foo_sub_type_alternative', + 'phpbb_ext_foo_type_alternative', + ), + $classes + ); + } + + public function test_absolute_directory_get_classes() + { + $classes = $this->finder + ->directory('/type/') + ->get_classes(); + + sort($classes); + $this->assertEquals( + array( + 'phpbb_ext_foo_type_alternative', + ), + $classes + ); + } + + public function test_sub_directory_get_classes() + { + $classes = $this->finder + ->directory('/sub/type') + ->get_classes(); + + sort($classes); + $this->assertEquals( + array( + 'phpbb_ext_foo_sub_type_alternative', + ), + $classes + ); + } + + public function test_cached_get_files() + { + $query = array( + 'default_path' => 'includes/foo', + 'default_suffix' => false, + 'default_directory' => 'bar', + 'suffix' => false, + 'directory' => false, + ); + + + $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', new phpbb_mock_cache(array( + '_extension_finder' => array( + md5(serialize($query)) => array('file_name'), + ), + ))); + + $classes = $finder + ->default_path($query['default_path']) + ->default_directory($query['default_directory']) + ->get_files(); + + sort($classes); + $this->assertEquals( + array('file_name'), + $classes + ); + } +} diff --git a/tests/extension/fixtures/extensions.xml b/tests/extension/fixtures/extensions.xml new file mode 100644 index 0000000000..f3acdd2575 --- /dev/null +++ b/tests/extension/fixtures/extensions.xml @@ -0,0 +1,15 @@ + + + + ext_name + ext_active + + foo + 1 + + + moo + 0 + +
+
diff --git a/tests/extension/includes/default/implementation.php b/tests/extension/includes/default/implementation.php new file mode 100644 index 0000000000..91d5f8aa2f --- /dev/null +++ b/tests/extension/includes/default/implementation.php @@ -0,0 +1,5 @@ +createXMLDataSet(dirname(__FILE__) . '/fixtures/extensions.xml'); + } + + protected function setUp() + { + parent::setUp(); + + // disable the regular class loader to replace it with one that loads + // test extensions + global $class_loader; + $class_loader->unregister(); + + $prefix = dirname(__FILE__) . '/'; + $this->class_loader = new phpbb_class_loader($prefix . '../../phpBB/includes/', $prefix . 'ext/'); + $this->class_loader->register(); + + $this->extension_manager = new phpbb_extension_manager( + $this->new_dbal(), + 'phpbb_ext', + $prefix, + '.php', + new phpbb_mock_cache + ); + } + + protected function tearDown() + { + global $class_loader; + $class_loader->register(); + } + + public function test_available() + { + $this->assertEquals(array('bar', 'foo', 'moo'), array_keys($this->extension_manager->all_available())); + } + + public function test_enabled() + { + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); + } + + public function test_configured() + { + $this->assertEquals(array('foo', 'moo'), array_keys($this->extension_manager->all_configured())); + } + + public function test_enable() + { + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); + $this->extension_manager->enable('bar'); + $this->assertEquals(array('bar', 'foo'), array_keys($this->extension_manager->all_enabled())); + $this->assertEquals(array('bar', 'foo', 'moo'), array_keys($this->extension_manager->all_configured())); + } + + public function test_disable() + { + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); + $this->extension_manager->disable('foo'); + $this->assertEquals(array(), array_keys($this->extension_manager->all_enabled())); + $this->assertEquals(array('foo', 'moo'), array_keys($this->extension_manager->all_configured())); + } + + public function test_purge() + { + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); + $this->assertEquals(array('foo', 'moo'), array_keys($this->extension_manager->all_configured())); + $this->extension_manager->purge('moo'); + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_configured())); + } +} diff --git a/tests/mock/extension_manager.php b/tests/mock/extension_manager.php new file mode 100644 index 0000000000..49d727db37 --- /dev/null +++ b/tests/mock/extension_manager.php @@ -0,0 +1,16 @@ +extensions = $extensions; + } +} From fabde989a2676c762f58e17b06772c9a3ba2f85e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Wed, 13 Jul 2011 08:22:27 -0400 Subject: [PATCH 02/67] [feature/extension-manager] Porting cron tasks over to the extension finder PHPBB3-10323 --- phpBB/includes/cron/manager.php | 123 ++++-------------- .../testext/cron}/dummy_task.php | 2 +- .../includes/cron/task/core/dummy_task.php | 23 ++++ .../cron/task/core}/second_dummy_task.php | 2 +- tests/cron/manager_test.php | 43 +++--- .../cron/task/core}/simple_not_runnable.php | 2 +- .../includes/cron/task/core/simple_ready.php | 8 ++ .../cron/task/core}/simple_should_not_run.php | 2 +- tests/cron/task2/testmod/simple_ready.php | 8 -- tests/extension/finder_test.php | 28 ++-- tests/mock/extension_manager.php | 4 +- 11 files changed, 100 insertions(+), 145 deletions(-) rename tests/cron/{task/testmod => ext/testext/cron}/dummy_task.php (80%) create mode 100644 tests/cron/includes/cron/task/core/dummy_task.php rename tests/cron/{task/testmod => includes/cron/task/core}/second_dummy_task.php (78%) rename tests/cron/{task2/testmod => root2/includes/cron/task/core}/simple_not_runnable.php (54%) create mode 100644 tests/cron/root2/includes/cron/task/core/simple_ready.php rename tests/cron/{task2/testmod => root2/includes/cron/task/core}/simple_should_not_run.php (53%) delete mode 100644 tests/cron/task2/testmod/simple_ready.php diff --git a/phpBB/includes/cron/manager.php b/phpBB/includes/cron/manager.php index 31be1a69cb..ae48e233e0 100644 --- a/phpBB/includes/cron/manager.php +++ b/phpBB/includes/cron/manager.php @@ -33,126 +33,53 @@ class phpbb_cron_manager protected $tasks = array(); /** - * Path to the root of directory tree with tasks. - * For bundled phpBB tasks, this is the path to includes/cron/tasks - * under phpBB root. - * @var string + * An extension manager to search for cron tasks in extensions. + * @var phpbb_extension_manager */ - protected $task_path; - - /** - * PHP file extension - * @var string - */ - protected $phpEx; - - /** - * Cache driver - * @var phpbb_cache_driver_interface - */ - protected $cache; + protected $extension_manager; /** * Constructor. Loads all available tasks. * - * Tasks will be looked up in directory tree rooted at $task_path. - * Task classes will be autoloaded and must be named according to - * autoloading naming conventions. To load cron tasks shipped with - * phpbb, pass $phpbb_root_path . 'includes/cron/task' as $task_path. + * Tasks will be looked up in the core task directory located in + * includes/cron/task/core/ and in extensions. Task classes will be + * autoloaded and must be named according to autoloading naming conventions. * - * If $cache is given, names of found cron tasks will be cached in it - * for one hour. Note that the cron task names are stored without - * namespacing; if two different phbb_cron_manager instances are - * constructed with different $task_path arguments but the same $cache, - * the second instance will use task names found by the first instance. + * Tasks in extensions must be located in a directory called cron or a subdir + * of a directory called cron. The class and filename must end in a _task + * suffix. * - * @param string $task_path Directory containing cron tasks - * @param string $phpEx PHP file extension - * @param phpbb_cache_driver_interface $cache Cache for task names (optional) - * @return void + * @param phpbb_extension_manager $extension_manager phpBB extension manager */ - public function __construct($task_path, $phpEx, phpbb_cache_driver_interface $cache = null) + public function __construct(phpbb_extension_manager $extension_manager) { - if (DIRECTORY_SEPARATOR != '/') - { - // Need this on some platforms since the code elsewhere uses / - // to separate directory components, but PHP iterators return - // paths with platform-specific directory separators. - $task_path = str_replace('/', DIRECTORY_SEPARATOR, $task_path); - } - - $this->task_path = $task_path; - $this->phpEx = $phpEx; - $this->cache = $cache; + $this->extension_manager = $extension_manager; $task_names = $this->find_cron_task_names(); $this->load_tasks($task_names); } /** - * Finds cron task names. + * Finds cron task names using the extension manager. * - * A cron task file must follow the naming convention: - * includes/cron/task/$mod/$name.php. - * $mod is core for tasks that are part of phpbb. - * Modifications should use their name as $mod. - * $name is the name of the cron task. - * Cron task is expected to be a class named phpbb_cron_task_${mod}_${name}. + * All PHP files in includes/cron/task/core/ are considered tasks. Tasks + * in extensions have to be located in a directory called cron or a subdir + * of a directory called cron. The class and filename must end in a _task + * suffix. * * @return array List of task names */ public function find_cron_task_names() { - if ($this->cache) - { - $task_names = $this->cache->get('_cron_tasks'); + $finder = $this->extension_manager->get_finder(); - if ($task_names !== false) - { - return $task_names; - } - } - - $task_names = array(); - $ext = '.' . $this->phpEx; - $ext_length = strlen($ext); - - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->task_path)); - - foreach ($iterator as $fileinfo) - { - $file = preg_replace('#^' . preg_quote($this->task_path, '#') . '#', '', $fileinfo->getPathname()); - - // skip directories and files direclty in the task root path - if ($fileinfo->isFile() && strpos($file, DIRECTORY_SEPARATOR) !== false) - { - $task_name = str_replace(DIRECTORY_SEPARATOR, '_', substr($file, 0, -$ext_length)); - if (substr($file, -$ext_length) == $ext && $this->is_valid_name($task_name)) - { - $task_names[] = 'phpbb_cron_task_' . $task_name; - } - } - } - - if ($this->cache) - { - $this->cache->put('_cron_tasks', $task_names, 3600); - } - - return $task_names; - } - - /** - * Checks whether $name is a valid identifier, and - * therefore part of valid cron task class name. - * - * @param string $name Name to check - * - * @return bool - */ - public function is_valid_name($name) - { - return (bool) preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $name); + return $finder + ->suffix('_task') + ->directory('/cron') + ->default_path('includes/cron/task/core/') + ->default_suffix('') + ->default_directory('') + ->get_classes(); } /** diff --git a/tests/cron/task/testmod/dummy_task.php b/tests/cron/ext/testext/cron/dummy_task.php similarity index 80% rename from tests/cron/task/testmod/dummy_task.php rename to tests/cron/ext/testext/cron/dummy_task.php index 5941157589..06546ada05 100644 --- a/tests/cron/task/testmod/dummy_task.php +++ b/tests/cron/ext/testext/cron/dummy_task.php @@ -7,7 +7,7 @@ * */ -class phpbb_cron_task_testmod_dummy_task extends phpbb_cron_task_base +class phpbb_ext_testext_cron_dummy_task extends phpbb_cron_task_base { public static $was_run = 0; diff --git a/tests/cron/includes/cron/task/core/dummy_task.php b/tests/cron/includes/cron/task/core/dummy_task.php new file mode 100644 index 0000000000..ddaf6a9b7c --- /dev/null +++ b/tests/cron/includes/cron/task/core/dummy_task.php @@ -0,0 +1,23 @@ +manager = new phpbb_cron_manager(dirname(__FILE__) . '/task/', 'php'); - $this->task_name = 'phpbb_cron_task_testmod_dummy_task'; + $this->extension_manager = new phpbb_mock_extension_manager( + dirname(__FILE__) . '/', + array( + 'testext' => array( + 'ext_name' => 'testext', + 'ext_active' => true, + 'ext_path' => dirname(__FILE__) . '/ext/testext/' + ), + )); + $this->manager = new phpbb_cron_manager($this->extension_manager); + $this->task_name = 'phpbb_cron_task_core_dummy_task'; } public function test_manager_finds_shipped_tasks() { $tasks = $this->manager->find_cron_task_names(); - $this->assertEquals(2, sizeof($tasks)); + $this->assertEquals(3, sizeof($tasks)); } public function test_manager_finds_shipped_task_by_name() @@ -45,7 +55,7 @@ class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase public function test_manager_finds_all_ready_tasks() { $tasks = $this->manager->find_all_ready_tasks(); - $this->assertEquals(2, sizeof($tasks)); + $this->assertEquals(3, sizeof($tasks)); } public function test_manager_finds_one_ready_task() @@ -54,21 +64,12 @@ class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase $this->assertInstanceOf('phpbb_cron_task_wrapper', $task); } - public function test_manager_finds_all_ready_tasks_cached() - { - $cache = new phpbb_mock_cache(array('_cron_tasks' => array($this->task_name))); - $manager = new phpbb_cron_manager(dirname(__FILE__) . '/../../phpBB/', 'php', $cache); - - $tasks = $manager->find_all_ready_tasks(); - $this->assertEquals(1, sizeof($tasks)); - } - public function test_manager_finds_only_ready_tasks() { - $manager = new phpbb_cron_manager(dirname(__FILE__) . '/task2/', 'php'); + $manager = new phpbb_cron_manager(new phpbb_mock_extension_manager(dirname(__FILE__) . '/root2/')); $tasks = $manager->find_all_ready_tasks(); $task_names = $this->tasks_to_names($tasks); - $this->assertEquals(array('phpbb_cron_task_testmod_simple_ready'), $task_names); + $this->assertEquals(array('phpbb_cron_task_core_simple_ready'), $task_names); } private function tasks_to_names($tasks) diff --git a/tests/cron/task2/testmod/simple_not_runnable.php b/tests/cron/root2/includes/cron/task/core/simple_not_runnable.php similarity index 54% rename from tests/cron/task2/testmod/simple_not_runnable.php rename to tests/cron/root2/includes/cron/task/core/simple_not_runnable.php index 54869fa1cc..837f28f1c0 100644 --- a/tests/cron/task2/testmod/simple_not_runnable.php +++ b/tests/cron/root2/includes/cron/task/core/simple_not_runnable.php @@ -1,6 +1,6 @@ extension_manager = new phpbb_mock_extension_manager(array( - 'foo' => array( - 'ext_name' => 'foo', - 'ext_active' => '1', - 'ext_path' => dirname(__FILE__) . '/ext/foo/', - ), - 'bar' => array( - 'ext_name' => 'bar', - 'ext_active' => '1', - 'ext_path' => dirname(__FILE__) . '/ext/bar/', - ), - )); + $this->extension_manager = new phpbb_mock_extension_manager( + dirname(__FILE__) . '/', + array( + 'foo' => array( + 'ext_name' => 'foo', + 'ext_active' => '1', + 'ext_path' => dirname(__FILE__) . '/ext/foo/', + ), + 'bar' => array( + 'ext_name' => 'bar', + 'ext_active' => '1', + 'ext_path' => dirname(__FILE__) . '/ext/bar/', + ), + )); - $this->finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/'); + $this->finder = $this->extension_manager->get_finder(); } public function test_suffix_get_classes() diff --git a/tests/mock/extension_manager.php b/tests/mock/extension_manager.php index 49d727db37..5155716181 100644 --- a/tests/mock/extension_manager.php +++ b/tests/mock/extension_manager.php @@ -9,8 +9,10 @@ class phpbb_mock_extension_manager extends phpbb_extension_manager { - public function __construct($extensions = array()) + public function __construct($phpbb_root_path, $extensions = array()) { + $this->phpbb_root_path = $phpbb_root_path; + $this->phpEx = '.php'; $this->extensions = $extensions; } } From fb943d4d6b39cea9825aab78e397eefe26cf7bdf Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 15 Aug 2011 19:34:30 -0400 Subject: [PATCH 03/67] [feature/extension-manager] Load the extension manager on all pages PHPBB3-10323 --- phpBB/common.php | 5 ++++- phpBB/download/file.php | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/phpBB/common.php b/phpBB/common.php index 7ff06e68d7..cc33b29a09 100644 --- a/phpBB/common.php +++ b/phpBB/common.php @@ -124,6 +124,9 @@ $config = new phpbb_config_db($db, $cache->get_driver(), CONFIG_TABLE); set_config(null, null, null, $config); set_config_count(null, null, null, $config); +// load extensions +$phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx", $cache->get_driver()); + $template_locator = new phpbb_template_locator(); $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator); @@ -138,5 +141,5 @@ foreach ($cache->obtain_hooks() as $hook) if (!$config['use_system_cron']) { - $cron = new phpbb_cron_manager($phpbb_root_path . 'includes/cron/task', $phpEx, $cache->get_driver()); + $cron = new phpbb_cron_manager($phpbb_extension_manager, $cache->get_driver()); } diff --git a/phpBB/download/file.php b/phpBB/download/file.php index 956af75c81..e13524f922 100644 --- a/phpBB/download/file.php +++ b/phpBB/download/file.php @@ -73,6 +73,9 @@ if (isset($_GET['avatar'])) set_config(null, null, null, $config); set_config_count(null, null, null, $config); + // load extensions + $phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx", $cache->get_driver()); + $filename = request_var('avatar', ''); $avatar_group = false; $exit = false; From dcc5ca53778184f0719b9ac0c221688ad03bd57e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 15 Aug 2011 20:00:47 -0400 Subject: [PATCH 04/67] [feature/extension-manager] Make search backends loadable from extensions Search backends are now required to be autoloadable. The database updater to 3.1 tries to guess the class name as phpbb_search_ which works for the default backends we ship. PHPBB3-10323 --- phpBB/includes/acp/acp_search.php | 29 +++++++++++-------- phpBB/includes/functions_posting.php | 9 ++---- .../includes/search/{search.php => base.php} | 4 +-- phpBB/includes/search/fulltext_mysql.php | 14 ++++----- phpBB/includes/search/fulltext_native.php | 17 +++++------ phpBB/install/database_update.php | 4 +++ phpBB/install/schemas/schema_data.sql | 2 +- phpBB/search.php | 11 +++---- 8 files changed, 44 insertions(+), 46 deletions(-) rename phpBB/includes/search/{search.php => base.php} (99%) diff --git a/phpBB/includes/acp/acp_search.php b/phpBB/includes/acp/acp_search.php index a3061ae2da..a6377f6b49 100644 --- a/phpBB/includes/acp/acp_search.php +++ b/phpBB/includes/acp/acp_search.php @@ -77,7 +77,8 @@ class acp_search continue; } - $name = ucfirst(strtolower(str_replace('_', ' ', $type))); + $name = $search->get_name(); + $selected = ($config['search_type'] == $type) ? ' selected="selected"' : ''; $search_options .= ''; @@ -275,7 +276,7 @@ class acp_search { trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING); } - $name = ucfirst(strtolower(str_replace('_', ' ', $this->state[0]))); + $name = $this->search->get_name(); $action = &$this->state[1]; @@ -454,7 +455,7 @@ class acp_search continue; } - $name = ucfirst(strtolower(str_replace('_', ' ', $type))); + $name = $search->get_name(); $data = array(); if (method_exists($search, 'index_stats')) @@ -553,8 +554,19 @@ class acp_search function get_search_types() { - global $phpbb_root_path, $phpEx; + global $phpbb_root_path, $phpEx, $phpbb_extension_manager; + $finder = $phpbb_extension_manager->get_finder(); + + return $finder + ->suffix('_backend') + ->directory('/search') + ->default_path('includes/search/') + ->default_suffix('') + ->default_directory('') + ->get_classes(); + +/* $search_types = array(); $dp = @opendir($phpbb_root_path . 'includes/search'); @@ -574,6 +586,7 @@ class acp_search } return $search_types; +*/ } function get_max_post_id() @@ -610,14 +623,6 @@ class acp_search { global $phpbb_root_path, $phpEx, $user; - if (!preg_match('#^\w+$#', $type) || !file_exists("{$phpbb_root_path}includes/search/$type.$phpEx")) - { - $error = $user->lang['NO_SUCH_SEARCH_MODULE']; - return $error; - } - - include_once("{$phpbb_root_path}includes/search/$type.$phpEx"); - if (!class_exists($type)) { $error = $user->lang['NO_SUCH_SEARCH_MODULE']; diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php index 4ca76344de..adc285cf6e 100644 --- a/phpBB/includes/functions_posting.php +++ b/phpBB/includes/functions_posting.php @@ -2350,16 +2350,11 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u if ($update_search_index && $data['enable_indexing']) { // Select the search method and do some additional checks to ensure it can actually be utilised - $search_type = basename($config['search_type']); - - if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx)) - { - trigger_error('NO_SUCH_SEARCH_MODULE'); - } + $search_type = $config['search_type']; if (!class_exists($search_type)) { - include("{$phpbb_root_path}includes/search/$search_type.$phpEx"); + trigger_error('NO_SUCH_SEARCH_MODULE'); } $error = false; diff --git a/phpBB/includes/search/search.php b/phpBB/includes/search/base.php similarity index 99% rename from phpBB/includes/search/search.php rename to phpBB/includes/search/base.php index 7c34ce9ff6..bb63957aba 100644 --- a/phpBB/includes/search/search.php +++ b/phpBB/includes/search/base.php @@ -24,12 +24,12 @@ define('SEARCH_RESULT_IN_CACHE', 1); define('SEARCH_RESULT_INCOMPLETE', 2); /** -* search_backend +* phpbb_search_base * optional base class for search plugins providing simple caching based on ACM * and functions to retrieve ignore_words and synonyms * @package search */ -class search_backend +class phpbb_search_base { var $ignore_words = array(); var $match_synonym = array(); diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php index 827205f20d..b9920da624 100644 --- a/phpBB/includes/search/fulltext_mysql.php +++ b/phpBB/includes/search/fulltext_mysql.php @@ -16,17 +16,12 @@ if (!defined('IN_PHPBB')) exit; } -/** -* @ignore -*/ -include_once($phpbb_root_path . 'includes/search/search.' . $phpEx); - /** * fulltext_mysql * Fulltext search for MySQL * @package search */ -class fulltext_mysql extends search_backend +class phpbb_search_fulltext_mysql extends phpbb_search_base { var $stats = array(); var $word_length = array(); @@ -36,7 +31,7 @@ class fulltext_mysql extends search_backend var $pcre_properties = false; var $mbstring_regex = false; - function fulltext_mysql(&$error) + public function __construct(&$error) { global $config; @@ -57,6 +52,11 @@ class fulltext_mysql extends search_backend $error = false; } + function get_name() + { + return 'MySQL Fulltext'; + } + /** * Checks for correct MySQL version and stores min/max word length in the config */ diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php index e749e86f68..7c792bba24 100644 --- a/phpBB/includes/search/fulltext_native.php +++ b/phpBB/includes/search/fulltext_native.php @@ -16,17 +16,12 @@ if (!defined('IN_PHPBB')) exit; } -/** -* @ignore -*/ -include_once($phpbb_root_path . 'includes/search/search.' . $phpEx); - /** * fulltext_native * phpBB's own db driven fulltext search, version 2 * @package search */ -class fulltext_native extends search_backend +class phpbb_search_fulltext_native extends phpbb_search_base { var $stats = array(); var $word_length = array(); @@ -41,10 +36,8 @@ class fulltext_native extends search_backend * Initialises the fulltext_native search backend with min/max word length and makes sure the UTF-8 normalizer is loaded. * * @param boolean|string &$error is passed by reference and should either be set to false on success or an error message on failure. - * - * @access public */ - function fulltext_native(&$error) + public function __construct(&$error) { global $phpbb_root_path, $phpEx, $config; @@ -58,10 +51,14 @@ class fulltext_native extends search_backend include($phpbb_root_path . 'includes/utf/utf_normalizer.' . $phpEx); } - $error = false; } + function get_name() + { + return 'phpBB Native Fulltext'; + } + /** * This function fills $this->search_query with the cleaned user search query. * diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index b2419df3c9..1f3c01ee3e 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -2106,6 +2106,10 @@ function change_database_data(&$no_updates, $version) // Changes from 3.1.0-dev to 3.1.0-A1 case '3.1.0-dev': + // try to guess the new auto loaded search class name + // works for native and mysql fulltext + set_config('search_type', 'phpbb_search_' . $config['search_type']); + set_config('use_system_cron', 0); $sql = 'UPDATE ' . GROUPS_TABLE . ' diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 5506922e17..bd7334f9d4 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -223,7 +223,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_block_size' INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_gc', '7200'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_interval', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_anonymous_interval', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_type', 'fulltext_native'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_type', 'phpbb_search_fulltext_native'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_store_results', '1800'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_allow_deny', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_allow_empty_referer', '1'); diff --git a/phpBB/search.php b/phpBB/search.php index 1d35dfb062..c6189051a3 100644 --- a/phpBB/search.php +++ b/phpBB/search.php @@ -73,7 +73,7 @@ switch ($search_id) login_box('', $user->lang['LOGIN_EXPLAIN_UNREADSEARCH']); } break; - + // The "new posts" search uses user_lastvisit which is user based, so it should require user to log in. case 'newposts': if ($user->data['user_id'] == ANONYMOUS) @@ -81,7 +81,7 @@ switch ($search_id) login_box('', $user->lang['LOGIN_EXPLAIN_NEWPOSTS']); } break; - + default: // There's nothing to do here for now ;) break; @@ -273,15 +273,12 @@ if ($keywords || $author || $author_id || $search_id || $submit) } // Select which method we'll use to obtain the post_id or topic_id information - $search_type = basename($config['search_type']); + $search_type = $config['search_type']; - if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx)) + if (!class_exists($search_type)) { trigger_error('NO_SUCH_SEARCH_MODULE'); } - - require("{$phpbb_root_path}includes/search/$search_type.$phpEx"); - // We do some additional checks in the module to ensure it can actually be utilised $error = false; $search = new $search_type($error); From 956860c21d2285ac325a48d6caf1e4fa45aca922 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 15 Aug 2011 20:07:35 -0400 Subject: [PATCH 05/67] [feature/extension-manager] Never cache extension finder queries in debug mode During development the detection of files should happen immediately and performance is less of a concern. PHPBB3-10323 --- phpBB/includes/extension/finder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index fb532e52f8..4180b84dd3 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -179,7 +179,7 @@ class phpbb_extension_finder { $query = md5(serialize($this->query)); - if ($cache && isset($this->cached_queries[$query])) + if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query])) { return $this->cached_queries[$query]; } From 989bd9cde7314024999f5b92fc2ff4572b9ac937 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 15 Aug 2011 20:55:29 -0400 Subject: [PATCH 06/67] [feature/extension-manager] Skip phpbb_search_base by checking for get_name() PHPBB3-10323 --- phpBB/includes/acp/acp_search.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/includes/acp/acp_search.php b/phpBB/includes/acp/acp_search.php index a6377f6b49..0c34d02ac7 100644 --- a/phpBB/includes/acp/acp_search.php +++ b/phpBB/includes/acp/acp_search.php @@ -623,7 +623,7 @@ class acp_search { global $phpbb_root_path, $phpEx, $user; - if (!class_exists($type)) + if (!class_exists($type) || !method_exists($type, 'get_name')) { $error = $user->lang['NO_SUCH_SEARCH_MODULE']; return $error; From 96209e022477d97b581b79cabace4caddd19501b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 15 Aug 2011 21:38:47 -0400 Subject: [PATCH 07/67] [feature/extension-manager] The class loader no longer knows about extensions Instead the class loader is instantiated twice. Once with the phpbb_ prefix and once with the phpbb_ext_ prefix. PHPBB3-10323 --- phpBB/common.php | 9 +++-- phpBB/download/file.php | 9 +++-- phpBB/includes/class_loader.php | 47 +++++++++--------------- phpBB/install/database_update.php | 9 +++-- phpBB/install/index.php | 9 +++-- tests/bootstrap.php | 6 ++- tests/class_loader/class_loader_test.php | 46 +++++++++++++++-------- tests/class_loader/ext/foo/class.php | 6 --- tests/extension/manager_test.php | 17 +-------- 9 files changed, 77 insertions(+), 81 deletions(-) delete mode 100644 tests/class_loader/ext/foo/class.php diff --git a/phpBB/common.php b/phpBB/common.php index cc33b29a09..61817972f9 100644 --- a/phpBB/common.php +++ b/phpBB/common.php @@ -96,13 +96,16 @@ require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler'); // Setup class loader first -$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); -$class_loader->register(); +$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx"); +$phpbb_class_loader_ext->register(); +$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx"); +$phpbb_class_loader->register(); // set up caching $cache_factory = new phpbb_cache_factory($acm_type); $cache = $cache_factory->get_service(); -$class_loader->set_cache($cache->get_driver()); +$phpbb_class_loader_ext->set_cache($cache->get_driver()); +$phpbb_class_loader->set_cache($cache->get_driver()); // Instantiate some basic classes $request = new phpbb_request(); diff --git a/phpBB/download/file.php b/phpBB/download/file.php index e13524f922..2a9c472ca7 100644 --- a/phpBB/download/file.php +++ b/phpBB/download/file.php @@ -46,13 +46,16 @@ if (isset($_GET['avatar'])) require($phpbb_root_path . 'includes/functions_download' . '.' . $phpEx); require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); - $class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); - $class_loader->register(); + $phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx"); + $phpbb_class_loader_ext->register(); + $phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx"); + $phpbb_class_loader->register(); // set up caching $cache_factory = new phpbb_cache_factory($acm_type); $cache = $cache_factory->get_service(); - $class_loader->set_cache($cache->get_driver()); + $phpbb_class_loader_ext->set_cache($cache->get_driver()); + $phpbb_class_loader->set_cache($cache->get_driver()); $request = new phpbb_request(); $db = new $sql_db(); diff --git a/phpBB/includes/class_loader.php b/phpBB/includes/class_loader.php index bcf1ba1650..46a271471a 100644 --- a/phpBB/includes/class_loader.php +++ b/phpBB/includes/class_loader.php @@ -31,25 +31,25 @@ if (!defined('IN_PHPBB')) */ class phpbb_class_loader { - private $core_path; - private $ext_path; + private $prefix; + private $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 core or extension path. + * file extension from the given path. * - * @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 string $prefix Required class name prefix for files to be loaded + * @param string $path Directory to load files from + * @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($core_path, $ext_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null) + public function __construct($prefix, $path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null) { - $this->core_path = $core_path; - $this->ext_path = $ext_path; + $this->prefix = $prefix; + $this->path = $path; $this->php_ext = $php_ext; $this->set_cache($cache); @@ -66,7 +66,7 @@ class phpbb_class_loader { if ($cache) { - $this->cached_paths = $cache->get('class_loader'); + $this->cached_paths = $cache->get('class_loader_' . $this->prefix); if ($this->cached_paths === false) { @@ -103,32 +103,21 @@ class phpbb_class_loader */ public function resolve_path($class) { - 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])) { - return $path_prefix . $this->cached_paths[$class] . $this->php_ext; + return $this->path . $this->cached_paths[$class] . $this->php_ext; } - if (!preg_match('/phpbb_[a-zA-Z0-9_]+/', $class)) + if (!preg_match('/^' . $this->prefix . '[a-zA-Z0-9_]+$/', $class)) { return false; } - $parts = explode('_', substr($class, $prefix_length)); + $parts = explode('_', substr($class, strlen($this->prefix))); $dirs = ''; - for ($i = 0, $n = sizeof($parts); $i < $n && is_dir($path_prefix . $dirs . $parts[$i]); $i++) + for ($i = 0, $n = sizeof($parts); $i < $n && is_dir($this->path . $dirs . $parts[$i]); $i++) { $dirs .= $parts[$i] . '/'; } @@ -141,7 +130,7 @@ class phpbb_class_loader $relative_path = $dirs . implode(array_slice($parts, $i, sizeof($parts) - $i), '_'); - if (!file_exists($path_prefix . $relative_path . $this->php_ext)) + if (!file_exists($this->path . $relative_path . $this->php_ext)) { return false; } @@ -149,10 +138,10 @@ class phpbb_class_loader if ($this->cache) { $this->cached_paths[$class] = $relative_path; - $this->cache->put('class_loader', $this->cached_paths); + $this->cache->put('class_loader_' . $this->prefix, $this->cached_paths); } - return $path_prefix . $relative_path . $this->php_ext; + return $this->path . $relative_path . $this->php_ext; } /** @@ -162,7 +151,7 @@ class phpbb_class_loader */ public function load_class($class) { - if (substr($class, 0, 6) === 'phpbb_') + if (substr($class, 0, strlen($this->prefix)) === $this->prefix) { $path = $this->resolve_path($class); diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 1f3c01ee3e..64dea37ef8 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -109,13 +109,16 @@ if (!defined('EXT_TABLE')) define('EXT_TABLE', $table_prefix . 'ext'); } -$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); -$class_loader->register(); +$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx"); +$phpbb_class_loader_ext->register(); +$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx"); +$phpbb_class_loader->register(); // set up caching $cache_factory = new phpbb_cache_factory($acm_type); $cache = $cache_factory->get_service(); -$class_loader->set_cache($cache->get_driver()); +$phpbb_class_loader_ext->set_cache($cache->get_driver()); +$phpbb_class_loader->set_cache($cache->get_driver()); $request = new phpbb_request(); $user = new user(); diff --git a/phpBB/install/index.php b/phpBB/install/index.php index fbe8ae63a7..0a46a06664 100644 --- a/phpBB/install/index.php +++ b/phpBB/install/index.php @@ -82,13 +82,16 @@ include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); require($phpbb_root_path . 'includes/functions_install.' . $phpEx); -$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.' . $phpEx); -$class_loader->register(); +$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx"); +$phpbb_class_loader_ext->register(); +$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx"); +$phpbb_class_loader->register(); // set up caching $cache_factory = new phpbb_cache_factory('file'); $cache = $cache_factory->get_service(); -$class_loader->set_cache($cache->get_driver()); +$phpbb_class_loader_ext->set_cache($cache->get_driver()); +$phpbb_class_loader->set_cache($cache->get_driver()); $request = new phpbb_request(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index fbe23c1835..a85091ce23 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -32,8 +32,10 @@ else require_once $phpbb_root_path . 'includes/constants.php'; require_once $phpbb_root_path . 'includes/class_loader.' . $phpEx; -$class_loader = new phpbb_class_loader($phpbb_root_path . 'includes/', $phpbb_root_path . 'ext/', '.php'); -$class_loader->register(); +$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".php"); +$phpbb_class_loader_ext->register(); +$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".php"); +$phpbb_class_loader->register(); require_once 'test_framework/phpbb_test_case_helpers.php'; require_once 'test_framework/phpbb_test_case.php'; diff --git a/tests/class_loader/class_loader_test.php b/tests/class_loader/class_loader_test.php index 7d5f57aac3..9744a1c703 100644 --- a/tests/class_loader/class_loader_test.php +++ b/tests/class_loader/class_loader_test.php @@ -13,20 +13,26 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase { public function setUp() { - global $class_loader; - $class_loader->unregister(); + global $phpbb_class_loader; + $phpbb_class_loader->unregister(); + + global $phpbb_class_loader_ext; + $phpbb_class_loader_ext->unregister(); } public function tearDown() { - global $class_loader; - $class_loader->register(); + global $phpbb_class_loader_ext; + $phpbb_class_loader_ext->register(); + + global $phpbb_class_loader; + $phpbb_class_loader->register(); } public function test_resolve_path() { $prefix = dirname(__FILE__) . '/'; - $class_loader = new phpbb_class_loader($prefix . 'includes/', $prefix . 'ext/'); + $class_loader = new phpbb_class_loader('phpbb_', $prefix . 'includes/'); $prefix .= 'includes/'; @@ -56,20 +62,19 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase $class_loader->resolve_path('phpbb_dir2'), 'Class with name of dir within dir (short class name)' ); - $this->assertEquals( - dirname(__FILE__) . '/ext/foo/class.php', - $class_loader->resolve_path('phpbb_ext_foo_class'), - 'Extension class' - ); } public function test_resolve_cached() { - $cacheMap = array('class_loader' => array('phpbb_a_cached_name' => 'a/cached_name')); - $cache = new phpbb_mock_cache($cacheMap); + $cache_map = array( + 'class_loader_phpbb_' => array('phpbb_a_cached_name' => 'a/cached_name'), + 'class_loader_phpbb_ext_' => array('phpbb_ext_foo' => 'foo'), + ); + $cache = new phpbb_mock_cache($cache_map); $prefix = dirname(__FILE__) . '/'; - $class_loader = new phpbb_class_loader($prefix . 'includes/', $prefix . 'ext/', '.php', $cache); + $class_loader = new phpbb_class_loader('phpbb_', $prefix . 'includes/', '.php', $cache); + $class_loader_ext = new phpbb_class_loader('phpbb_ext_', $prefix . 'includes/', '.php', $cache); $prefix .= 'includes/'; @@ -79,13 +84,22 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase 'Class in a directory' ); + $this->assertFalse($class_loader->resolve_path('phpbb_ext_foo')); + $this->assertFalse($class_loader_ext->resolve_path('phpbb_a_cached_name')); + $this->assertEquals( $prefix . 'a/cached_name.php', $class_loader->resolve_path('phpbb_a_cached_name'), - 'Class in a directory' + 'Cached class found' ); - $cacheMap['class_loader']['phpbb_dir_class_name'] = 'dir/class_name'; - $cache->check($this, $cacheMap); + $this->assertEquals( + $prefix . 'foo.php', + $class_loader_ext->resolve_path('phpbb_ext_foo'), + 'Cached class found in alternative loader' + ); + + $cache_map['class_loader_phpbb_']['phpbb_dir_class_name'] = 'dir/class_name'; + $cache->check($this, $cache_map); } } diff --git a/tests/class_loader/ext/foo/class.php b/tests/class_loader/ext/foo/class.php deleted file mode 100644 index 7b1555c98d..0000000000 --- a/tests/class_loader/ext/foo/class.php +++ /dev/null @@ -1,6 +0,0 @@ -unregister(); - - $prefix = dirname(__FILE__) . '/'; - $this->class_loader = new phpbb_class_loader($prefix . '../../phpBB/includes/', $prefix . 'ext/'); - $this->class_loader->register(); - $this->extension_manager = new phpbb_extension_manager( $this->new_dbal(), 'phpbb_ext', - $prefix, + dirname(__FILE__) . '/', '.php', new phpbb_mock_cache ); } - protected function tearDown() - { - global $class_loader; - $class_loader->register(); - } - public function test_available() { $this->assertEquals(array('bar', 'foo', 'moo'), array_keys($this->extension_manager->all_available())); From 5d5030a48be3d65df85d78e26690085c0889c6e3 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 21 Aug 2011 02:57:01 -0400 Subject: [PATCH 08/67] [feature/extension-manager] Remove cron's dependency on the extension manager. Instead a separate cron provider supplies the manager with tasks from the extension finder. PHPBB3-10323 --- phpBB/common.php | 2 +- phpBB/cron.php | 2 +- phpBB/includes/cron/manager.php | 48 +--------- phpBB/includes/cron/provider.php | 92 +++++++++++++++++++ tests/cron/manager_test.php | 33 +++---- tests/cron/provider_test.php | 45 +++++++++ .../core => tasks}/simple_not_runnable.php | 0 .../cron/task/core => tasks}/simple_ready.php | 0 .../core => tasks}/simple_should_not_run.php | 0 9 files changed, 156 insertions(+), 66 deletions(-) create mode 100644 phpBB/includes/cron/provider.php create mode 100644 tests/cron/provider_test.php rename tests/cron/{root2/includes/cron/task/core => tasks}/simple_not_runnable.php (100%) rename tests/cron/{root2/includes/cron/task/core => tasks}/simple_ready.php (100%) rename tests/cron/{root2/includes/cron/task/core => tasks}/simple_should_not_run.php (100%) diff --git a/phpBB/common.php b/phpBB/common.php index 61817972f9..3cc9e57e46 100644 --- a/phpBB/common.php +++ b/phpBB/common.php @@ -144,5 +144,5 @@ foreach ($cache->obtain_hooks() as $hook) if (!$config['use_system_cron']) { - $cron = new phpbb_cron_manager($phpbb_extension_manager, $cache->get_driver()); + $cron = new phpbb_cron_manager(new phpbb_cron_provider($phpbb_extension_manager), $cache->get_driver()); } diff --git a/phpBB/cron.php b/phpBB/cron.php index cc5964218a..6633a6c3fd 100644 --- a/phpBB/cron.php +++ b/phpBB/cron.php @@ -62,7 +62,7 @@ function do_cron($cron_lock, $run_tasks) if ($config['use_system_cron']) { - $cron = new phpbb_cron_manager($phpbb_root_path . 'includes/cron/task', $phpEx, $cache->get_driver()); + $cron = new phpbb_cron_manager(new phpbb_cron_provider($phpbb_extension_manager), $cache->get_driver()); } else { diff --git a/phpBB/includes/cron/manager.php b/phpBB/includes/cron/manager.php index ae48e233e0..a0bf018b33 100644 --- a/phpBB/includes/cron/manager.php +++ b/phpBB/includes/cron/manager.php @@ -32,65 +32,25 @@ class phpbb_cron_manager */ protected $tasks = array(); - /** - * An extension manager to search for cron tasks in extensions. - * @var phpbb_extension_manager - */ - protected $extension_manager; - /** * Constructor. Loads all available tasks. * - * Tasks will be looked up in the core task directory located in - * includes/cron/task/core/ and in extensions. Task classes will be - * autoloaded and must be named according to autoloading naming conventions. - * - * Tasks in extensions must be located in a directory called cron or a subdir - * of a directory called cron. The class and filename must end in a _task - * suffix. - * - * @param phpbb_extension_manager $extension_manager phpBB extension manager + * @param array|Traversable $task_names Provides an iterable set of task names */ - public function __construct(phpbb_extension_manager $extension_manager) + public function __construct($task_names) { - $this->extension_manager = $extension_manager; - - $task_names = $this->find_cron_task_names(); $this->load_tasks($task_names); } - /** - * Finds cron task names using the extension manager. - * - * All PHP files in includes/cron/task/core/ are considered tasks. Tasks - * in extensions have to be located in a directory called cron or a subdir - * of a directory called cron. The class and filename must end in a _task - * suffix. - * - * @return array List of task names - */ - public function find_cron_task_names() - { - $finder = $this->extension_manager->get_finder(); - - return $finder - ->suffix('_task') - ->directory('/cron') - ->default_path('includes/cron/task/core/') - ->default_suffix('') - ->default_directory('') - ->get_classes(); - } - /** * Loads tasks given by name, wraps them * and puts them into $this->tasks. * - * @param array $task_names Array of strings + * @param array|Traversable $task_names Array of strings * * @return void */ - public function load_tasks(array $task_names) + public function load_tasks($task_names) { foreach ($task_names as $task_name) { diff --git a/phpBB/includes/cron/provider.php b/phpBB/includes/cron/provider.php new file mode 100644 index 0000000000..9936da3f55 --- /dev/null +++ b/phpBB/includes/cron/provider.php @@ -0,0 +1,92 @@ +extension_manager = $extension_manager; + + $this->task_names = $this->find_cron_task_names(); + } + + /** + * Finds cron task names using the extension manager. + * + * All PHP files in includes/cron/task/core/ are considered tasks. Tasks + * in extensions have to be located in a directory called cron or a subdir + * of a directory called cron. The class and filename must end in a _task + * suffix. + * + * @return array List of task names + */ + public function find_cron_task_names() + { + $finder = $this->extension_manager->get_finder(); + + return $finder + ->suffix('_task') + ->directory('/cron') + ->default_path('includes/cron/task/core/') + ->default_suffix('') + ->default_directory('') + ->get_classes(); + } + + /** + * Retrieve an iterator over all task names + * + * @return ArrayIterator An iterator for the array of task names + */ + public function getIterator() + { + return new ArrayIterator($this->task_names); + } +} diff --git a/tests/cron/manager_test.php b/tests/cron/manager_test.php index 80f2cd55a8..80c92e234b 100644 --- a/tests/cron/manager_test.php +++ b/tests/cron/manager_test.php @@ -11,33 +11,22 @@ require_once dirname(__FILE__) . '/../mock/extension_manager.php'; require_once dirname(__FILE__) . '/includes/cron/task/core/dummy_task.php'; require_once dirname(__FILE__) . '/includes/cron/task/core/second_dummy_task.php'; require_once dirname(__FILE__) . '/ext/testext/cron/dummy_task.php'; -require_once dirname(__FILE__) . '/root2/includes/cron/task/core/simple_ready.php'; -require_once dirname(__FILE__) . '/root2/includes/cron/task/core/simple_not_runnable.php'; -require_once dirname(__FILE__) . '/root2/includes/cron/task/core/simple_should_not_run.php'; +require_once dirname(__FILE__) . '/tasks/simple_ready.php'; +require_once dirname(__FILE__) . '/tasks/simple_not_runnable.php'; +require_once dirname(__FILE__) . '/tasks/simple_should_not_run.php'; class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase { public function setUp() { - $this->extension_manager = new phpbb_mock_extension_manager( - dirname(__FILE__) . '/', - array( - 'testext' => array( - 'ext_name' => 'testext', - 'ext_active' => true, - 'ext_path' => dirname(__FILE__) . '/ext/testext/' - ), - )); - $this->manager = new phpbb_cron_manager($this->extension_manager); + $this->manager = new phpbb_cron_manager(array( + 'phpbb_cron_task_core_dummy_task', + 'phpbb_cron_task_core_second_dummy_task', + 'phpbb_ext_testext_cron_dummy_task', + )); $this->task_name = 'phpbb_cron_task_core_dummy_task'; } - public function test_manager_finds_shipped_tasks() - { - $tasks = $this->manager->find_cron_task_names(); - $this->assertEquals(3, sizeof($tasks)); - } - public function test_manager_finds_shipped_task_by_name() { $task = $this->manager->find_task($this->task_name); @@ -66,7 +55,11 @@ class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase public function test_manager_finds_only_ready_tasks() { - $manager = new phpbb_cron_manager(new phpbb_mock_extension_manager(dirname(__FILE__) . '/root2/')); + $manager = new phpbb_cron_manager(array( + 'phpbb_cron_task_core_simple_ready', + 'phpbb_cron_task_core_simple_not_runnable', + 'phpbb_cron_task_core_simple_should_not_run', + )); $tasks = $manager->find_all_ready_tasks(); $task_names = $this->tasks_to_names($tasks); $this->assertEquals(array('phpbb_cron_task_core_simple_ready'), $task_names); diff --git a/tests/cron/provider_test.php b/tests/cron/provider_test.php new file mode 100644 index 0000000000..781425e6ab --- /dev/null +++ b/tests/cron/provider_test.php @@ -0,0 +1,45 @@ +extension_manager = new phpbb_mock_extension_manager( + dirname(__FILE__) . '/', + array( + 'testext' => array( + 'ext_name' => 'testext', + 'ext_active' => true, + 'ext_path' => dirname(__FILE__) . '/ext/testext/' + ), + )); + $this->provider = new phpbb_cron_provider($this->extension_manager); + } + + public function test_manager_finds_shipped_tasks() + { + $task_iterator = $this->provider->find_cron_task_names(); + + $tasks = array(); + foreach ($task_iterator as $task) + { + $tasks[] = $task; + } + sort($tasks); + + $this->assertEquals(array( + 'phpbb_cron_task_core_dummy_task', + 'phpbb_cron_task_core_second_dummy_task', + 'phpbb_ext_testext_cron_dummy_task', + ), $tasks); + } +} diff --git a/tests/cron/root2/includes/cron/task/core/simple_not_runnable.php b/tests/cron/tasks/simple_not_runnable.php similarity index 100% rename from tests/cron/root2/includes/cron/task/core/simple_not_runnable.php rename to tests/cron/tasks/simple_not_runnable.php diff --git a/tests/cron/root2/includes/cron/task/core/simple_ready.php b/tests/cron/tasks/simple_ready.php similarity index 100% rename from tests/cron/root2/includes/cron/task/core/simple_ready.php rename to tests/cron/tasks/simple_ready.php diff --git a/tests/cron/root2/includes/cron/task/core/simple_should_not_run.php b/tests/cron/tasks/simple_should_not_run.php similarity index 100% rename from tests/cron/root2/includes/cron/task/core/simple_should_not_run.php rename to tests/cron/tasks/simple_should_not_run.php From c6fd8d9c9bfad943b9ed7d8f5f4407c13b8939d9 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 21 Aug 2011 03:02:48 -0400 Subject: [PATCH 09/67] [feature/extension-manager] Add missing newlines at end of files PHPBB3-10323 --- tests/extension/ext/bar/my/hidden_class.php | 2 +- tests/extension/ext/foo/a_class.php | 2 +- tests/extension/ext/foo/b_class.php | 2 +- tests/extension/ext/foo/foo.php | 2 +- tests/extension/ext/foo/type/alternative.php | 2 +- tests/extension/ext/moo/feature_class.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/extension/ext/bar/my/hidden_class.php b/tests/extension/ext/bar/my/hidden_class.php index b3c910a8c2..0261d7c59a 100644 --- a/tests/extension/ext/bar/my/hidden_class.php +++ b/tests/extension/ext/bar/my/hidden_class.php @@ -2,4 +2,4 @@ class phpbb_ext_bar_my_hidden_class { -} \ No newline at end of file +} diff --git a/tests/extension/ext/foo/a_class.php b/tests/extension/ext/foo/a_class.php index 03253139f9..b7be1ad654 100644 --- a/tests/extension/ext/foo/a_class.php +++ b/tests/extension/ext/foo/a_class.php @@ -2,4 +2,4 @@ class phpbb_ext_foo_a_class { -} \ No newline at end of file +} diff --git a/tests/extension/ext/foo/b_class.php b/tests/extension/ext/foo/b_class.php index 04644a9d9d..4645266122 100644 --- a/tests/extension/ext/foo/b_class.php +++ b/tests/extension/ext/foo/b_class.php @@ -2,4 +2,4 @@ class phpbb_ext_foo_b_class { -} \ No newline at end of file +} diff --git a/tests/extension/ext/foo/foo.php b/tests/extension/ext/foo/foo.php index 78a8c95f65..bbf01606ce 100644 --- a/tests/extension/ext/foo/foo.php +++ b/tests/extension/ext/foo/foo.php @@ -2,4 +2,4 @@ class phpbb_ext_foo extends phpbb_extension_base { -} \ No newline at end of file +} diff --git a/tests/extension/ext/foo/type/alternative.php b/tests/extension/ext/foo/type/alternative.php index b43a293b1d..404b66b965 100644 --- a/tests/extension/ext/foo/type/alternative.php +++ b/tests/extension/ext/foo/type/alternative.php @@ -2,4 +2,4 @@ class phpbb_ext_foo_type_alternative { -} \ No newline at end of file +} diff --git a/tests/extension/ext/moo/feature_class.php b/tests/extension/ext/moo/feature_class.php index 20ea13054f..bf7ba40d84 100644 --- a/tests/extension/ext/moo/feature_class.php +++ b/tests/extension/ext/moo/feature_class.php @@ -2,4 +2,4 @@ class phpbb_ext_moo_feature_class { -} \ No newline at end of file +} From 60ad0e21b5d4f940740650df69b7134a573f2a97 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 21 Aug 2011 03:06:56 -0400 Subject: [PATCH 10/67] [feature/extension-manager] Remove the ext_active index for lack of specificity PHPBB3-10323 --- phpBB/develop/create_schema_files.php | 1 - phpBB/install/database_update.php | 1 - phpBB/install/schemas/firebird_schema.sql | 1 - phpBB/install/schemas/mssql_schema.sql | 3 --- phpBB/install/schemas/mysql_40_schema.sql | 3 +-- phpBB/install/schemas/mysql_41_schema.sql | 3 +-- phpBB/install/schemas/oracle_schema.sql | 2 -- phpBB/install/schemas/postgres_schema.sql | 1 - phpBB/install/schemas/sqlite_schema.sql | 1 - 9 files changed, 2 insertions(+), 14 deletions(-) diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index 9f2015f38e..bc1e28572d 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -1038,7 +1038,6 @@ function get_schema_struct() ), 'KEYS' => array( 'ext_name' => array('UNIQUE', 'ext_name'), - 'ext_active' => array('INDEX', 'ext_active'), ), ); diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 64dea37ef8..45de19c918 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -1063,7 +1063,6 @@ function database_update_info() ), 'KEYS' => array( 'ext_name' => array('UNIQUE', 'ext_name'), - 'ext_active' => array('INDEX', 'ext_active'), ), ), ), diff --git a/phpBB/install/schemas/firebird_schema.sql b/phpBB/install/schemas/firebird_schema.sql index 5f5aaaaa45..4952b6bbdf 100644 --- a/phpBB/install/schemas/firebird_schema.sql +++ b/phpBB/install/schemas/firebird_schema.sql @@ -288,7 +288,6 @@ CREATE TABLE phpbb_ext ( );; CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext(ext_name);; -CREATE INDEX phpbb_ext_ext_active ON phpbb_ext(ext_active);; # Table: 'phpbb_extensions' CREATE TABLE phpbb_extensions ( diff --git a/phpBB/install/schemas/mssql_schema.sql b/phpBB/install/schemas/mssql_schema.sql index 052531993f..76f3fc6491 100644 --- a/phpBB/install/schemas/mssql_schema.sql +++ b/phpBB/install/schemas/mssql_schema.sql @@ -370,9 +370,6 @@ GO CREATE UNIQUE INDEX [ext_name] ON [phpbb_ext]([ext_name]) ON [PRIMARY] GO -CREATE INDEX [ext_active] ON [phpbb_ext]([ext_active]) ON [PRIMARY] -GO - /* Table: 'phpbb_extensions' diff --git a/phpBB/install/schemas/mysql_40_schema.sql b/phpBB/install/schemas/mysql_40_schema.sql index 03402d7ed1..a1b282418d 100644 --- a/phpBB/install/schemas/mysql_40_schema.sql +++ b/phpBB/install/schemas/mysql_40_schema.sql @@ -195,8 +195,7 @@ CREATE TABLE phpbb_drafts ( CREATE TABLE phpbb_ext ( ext_name varbinary(255) DEFAULT '' NOT NULL, ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL, - UNIQUE ext_name (ext_name), - KEY ext_active (ext_active) + UNIQUE ext_name (ext_name) ); diff --git a/phpBB/install/schemas/mysql_41_schema.sql b/phpBB/install/schemas/mysql_41_schema.sql index 8876b52a30..2f2de17dcb 100644 --- a/phpBB/install/schemas/mysql_41_schema.sql +++ b/phpBB/install/schemas/mysql_41_schema.sql @@ -195,8 +195,7 @@ CREATE TABLE phpbb_drafts ( CREATE TABLE phpbb_ext ( ext_name varchar(255) DEFAULT '' NOT NULL, ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL, - UNIQUE ext_name (ext_name), - KEY ext_active (ext_active) + UNIQUE ext_name (ext_name) ) CHARACTER SET `utf8` COLLATE `utf8_bin`; diff --git a/phpBB/install/schemas/oracle_schema.sql b/phpBB/install/schemas/oracle_schema.sql index df31f6d3d6..f0a958d2f6 100644 --- a/phpBB/install/schemas/oracle_schema.sql +++ b/phpBB/install/schemas/oracle_schema.sql @@ -417,8 +417,6 @@ CREATE TABLE phpbb_ext ( ) / -CREATE INDEX phpbb_ext_ext_active ON phpbb_ext (ext_active) -/ /* Table: 'phpbb_extensions' diff --git a/phpBB/install/schemas/postgres_schema.sql b/phpBB/install/schemas/postgres_schema.sql index dbe891a3ac..956167ff39 100644 --- a/phpBB/install/schemas/postgres_schema.sql +++ b/phpBB/install/schemas/postgres_schema.sql @@ -321,7 +321,6 @@ CREATE TABLE phpbb_ext ( ); CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name); -CREATE INDEX phpbb_ext_ext_active ON phpbb_ext (ext_active); /* Table: 'phpbb_extensions' diff --git a/phpBB/install/schemas/sqlite_schema.sql b/phpBB/install/schemas/sqlite_schema.sql index 3c1d476daa..223dbc3c02 100644 --- a/phpBB/install/schemas/sqlite_schema.sql +++ b/phpBB/install/schemas/sqlite_schema.sql @@ -193,7 +193,6 @@ CREATE TABLE phpbb_ext ( ); CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name); -CREATE INDEX phpbb_ext_ext_active ON phpbb_ext (ext_active); # Table: 'phpbb_extensions' CREATE TABLE phpbb_extensions ( From f6632fcfd08650f13560529a6a04c152aefd4e3c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 22 Aug 2011 02:17:00 -0400 Subject: [PATCH 11/67] [feature/extension-manager] Add filename prefix matching in extension finder PHPBB3-10323 --- phpBB/includes/extension/finder.php | 47 ++++++++++++++++++++++++++--- tests/extension/finder_test.php | 20 ++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index 4180b84dd3..fbc8a3aefb 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -50,8 +50,10 @@ class phpbb_extension_finder $this->query = array( 'default_path' => false, 'default_suffix' => false, + 'default_prefix' => false, 'default_directory' => false, 'suffix' => false, + 'prefix' => false, 'directory' => false, ); @@ -76,7 +78,7 @@ class phpbb_extension_finder * Automatically sets the default_suffix if its value does not differ from * the current suffix. * - * @param string $default_path A filename suffix + * @param string $suffix A filename suffix * @return phpbb_extension_finder This object for chaining calls */ public function suffix($suffix) @@ -102,6 +104,38 @@ class phpbb_extension_finder return $this; } + /** + * Sets a prefix all files found in extensions must match + * + * Automatically sets the default_prefix if its value does not differ from + * the current prefix. + * + * @param string $prefix A filename prefix + * @return phpbb_extension_finder This object for chaining calls + */ + public function prefix($prefix) + { + if ($this->query['default_prefix'] === $this->query['prefix']) + { + $this->query['default_prefix'] = $prefix; + } + + $this->query['prefix'] = $prefix; + return $this; + } + + /** + * Sets a prefix all files found in the default path must match + * + * @param string $default_prefix A filename prefix + * @return phpbb_extension_finder This object for chaining calls + */ + public function default_prefix($default_prefix) + { + $this->query['default_prefix'] = $default_prefix; + return $this; + } + /** * Sets a directory all files found in extensions must be contained in * @@ -202,16 +236,18 @@ class phpbb_extension_finder if ($name === '/') { - $prefix = $this->query['default_path']; + $location = $this->query['default_path']; $name = ''; $suffix = $this->query['default_suffix']; + $prefix = $this->query['default_prefix']; $directory = $this->query['default_directory']; } else { - $prefix = 'ext/'; + $location = 'ext/'; $name .= '/'; $suffix = $this->query['suffix']; + $prefix = $this->query['prefix']; $directory = $this->query['directory']; } @@ -226,10 +262,11 @@ class phpbb_extension_finder { $relative_path = $iterator->getInnerIterator()->getSubPathname(); - if ((!$suffix || substr($relative_path, -strlen($suffix)) == $suffix) && + if ((!$suffix || substr($relative_path, -strlen($suffix)) === $suffix) && + (!$prefix || substr($file_info->getFilename(), 0, strlen($prefix)) === $prefix) && (!$directory || preg_match($directory_pattern, DIRECTORY_SEPARATOR . $relative_path))) { - $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $prefix . $name . $relative_path); + $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . $relative_path); } } } diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php index b0c98da554..4acfe53937 100644 --- a/tests/extension/finder_test.php +++ b/tests/extension/finder_test.php @@ -55,6 +55,24 @@ class phpbb_extension_finder_test extends phpbb_test_case ); } + public function test_prefix_get_classes() + { + $classes = $this->finder + ->default_path('includes/default/') + ->prefix('hidden_') + ->default_prefix('') + ->get_classes(); + + sort($classes); + $this->assertEquals( + array( + 'phpbb_default_implementation', + 'phpbb_ext_bar_my_hidden_class', + ), + $classes + ); + } + public function test_directory_get_classes() { $classes = $this->finder @@ -109,8 +127,10 @@ class phpbb_extension_finder_test extends phpbb_test_case $query = array( 'default_path' => 'includes/foo', 'default_suffix' => false, + 'default_prefix' => false, 'default_directory' => 'bar', 'suffix' => false, + 'prefix' => false, 'directory' => false, ); From 4844b007771f71973db2fa440d3c8ef9057d1b02 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 22 Aug 2011 03:19:17 -0400 Subject: [PATCH 12/67] [feature/extension-manager] Load (A/U/M)CP modules from extensions To avoid large bc breaking changes, modules in the old includes directory structure still follow the same naming conventions. Modules in extensions have to be placed in an xcp/ folder and need a _module suffix. The corresponding info file is in the same directory but with an _info suffix. PHPBB3-10323 --- phpBB/includes/acp/acp_modules.php | 87 ++++++++++++++++++----------- phpBB/includes/functions_module.php | 86 ++++++++++++++-------------- 2 files changed, 96 insertions(+), 77 deletions(-) diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php index 52033b590c..2a533056b4 100644 --- a/phpBB/includes/acp/acp_modules.php +++ b/phpBB/includes/acp/acp_modules.php @@ -316,7 +316,7 @@ class acp_modules } // Name options - $s_name_options .= ''; + $s_name_options .= ''; $template->assign_block_vars('m_names', array('NAME' => $option, 'A_NAME' => addslashes($option))); @@ -480,7 +480,7 @@ class acp_modules foreach ($module_infos as $option => $values) { // Name options - $s_install_options .= ''; + $s_install_options .= ''; // Build module modes foreach ($values['modes'] as $m_mode => $m_values) @@ -539,57 +539,78 @@ class acp_modules if (!$module) { - $dh = @opendir($directory); + global $phpbb_extension_manager; - if (!$dh) - { - return $fileinfo; - } + $finder = $phpbb_extension_manager->get_finder(); - while (($file = readdir($dh)) !== false) + $modules = $finder + ->suffix('_module') + ->directory("/$module_class") + ->default_path("includes/$module_class/info/") + ->default_suffix('') + ->default_prefix($module_class . '_') + ->default_directory('') + ->get_classes(); + + foreach ($modules as $module) { - // Is module? - if (preg_match('/^' . $module_class . '_.+\.' . $phpEx . '$/', $file)) + // If the class does not exist it might be following the old + // format. phpbb_acp_info_acp_foo needs to be turned into + // acp_foo_info and the respective file has to be included + // manually because it does not support auto loading + if (!class_exists($module)) { - $class = str_replace(".$phpEx", '', $file) . '_info'; - - if (!class_exists($class)) + $info_class = str_replace("phpbb_{$module_class}_info_", '', $module) . '_info'; + if (file_exists($directory . $info_class . '.' . $phpEx)) { - include($directory . $file); - } - - // Get module title tag - if (class_exists($class)) - { - $c_class = new $class(); - $module_info = $c_class->module(); - $fileinfo[str_replace($module_class . '_', '', $module_info['filename'])] = $module_info; + include($directory . $info_class . '.' . $phpEx); } } + else + { + $info_class = preg_replace('/_module$/', '_info', $module); + } + + if (class_exists($info_class)) + { + $info = new $info_class(); + $module_info = $info->module(); + + $main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $module; + + $fileinfo[$main_class] = $module_info; + } } - closedir($dh); ksort($fileinfo); } else { - $filename = $module_class . '_' . basename($module); - $class = $module_class . '_' . basename($module) . '_info'; - - if (!class_exists($class)) + if (!class_exists($module)) { - include($directory . $filename . '.' . $phpEx); + if (file_exists($directory . $module . '.' . $phpEx)) + { + include($directory . $module . '.' . $phpEx); + } + $info_class = $module . '_info'; + } + else + { + $info_class = preg_replace('/_module$/', '_info', $module); } // Get module title tag - if (class_exists($class)) + if (class_exists($info_class)) { - $c_class = new $class(); - $module_info = $c_class->module(); - $fileinfo[str_replace($module_class . '_', '', $module_info['filename'])] = $module_info; + $info = new $info_class(); + $module_info = $info->module(); + + $main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $module; + + $fileinfo[$main_class] = $module_info; } } - + return $fileinfo; } diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php index 09c54422b0..1a6b57794a 100644 --- a/phpBB/includes/functions_module.php +++ b/phpBB/includes/functions_module.php @@ -440,7 +440,8 @@ class p_master trigger_error('Module not accessible', E_USER_ERROR); } - if (!class_exists("{$this->p_class}_$this->p_name")) + // new modules use the full class names, old ones are always called _, e.g. acp_board + if (!class_exists($this->p_name) && !class_exists("{$this->p_class}_$this->p_name")) { if (!file_exists("$module_path/{$this->p_class}_$this->p_name.$phpEx")) { @@ -453,62 +454,59 @@ class p_master { trigger_error("Module file $module_path/{$this->p_class}_$this->p_name.$phpEx does not contain correct class [{$this->p_class}_$this->p_name]", E_USER_ERROR); } + } - if (!empty($mode)) + if (!empty($mode)) + { + $this->p_mode = $mode; + } + + // Create a new instance of the desired module ... + $class_name = (class_exists($this->p_name)) ? $this->p_name : "{$this->p_class}_$this->p_name"; + + $this->module = new $class_name($this); + + // We pre-define the action parameter we are using all over the place + if (defined('IN_ADMIN')) + { + // Is first module automatically enabled a duplicate and the category not passed yet? + if (!$icat && $this->module_ary[$this->active_module_row_id]['is_duplicate']) { - $this->p_mode = $mode; + $icat = $this->module_ary[$this->active_module_row_id]['parent']; } - // Create a new instance of the desired module ... if it has a - // constructor it will of course be executed - $instance = "{$this->p_class}_$this->p_name"; - - $this->module = new $instance($this); - - // We pre-define the action parameter we are using all over the place - if (defined('IN_ADMIN')) + // Not being able to overwrite ;) + $this->module->u_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i={$this->p_name}") . (($icat) ? '&icat=' . $icat : '') . "&mode={$this->p_mode}"; + } + else + { + // If user specified the module url we will use it... + if ($module_url !== false) { - // Is first module automatically enabled a duplicate and the category not passed yet? - if (!$icat && $this->module_ary[$this->active_module_row_id]['is_duplicate']) - { - $icat = $this->module_ary[$this->active_module_row_id]['parent']; - } - - // Not being able to overwrite ;) - $this->module->u_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i={$this->p_name}") . (($icat) ? '&icat=' . $icat : '') . "&mode={$this->p_mode}"; + $this->module->u_action = $module_url; } else { - // If user specified the module url we will use it... - if ($module_url !== false) - { - $this->module->u_action = $module_url; - } - else - { - $this->module->u_action = $phpbb_root_path . (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name']; - } - - $this->module->u_action = append_sid($this->module->u_action, "i={$this->p_name}") . (($icat) ? '&icat=' . $icat : '') . "&mode={$this->p_mode}"; + $this->module->u_action = $phpbb_root_path . (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name']; } - // Add url_extra parameter to u_action url - if (!empty($this->module_ary) && $this->active_module !== false && $this->module_ary[$this->active_module_row_id]['url_extra']) - { - $this->module->u_action .= $this->module_ary[$this->active_module_row_id]['url_extra']; - } + $this->module->u_action = append_sid($this->module->u_action, "i={$this->p_name}") . (($icat) ? '&icat=' . $icat : '') . "&mode={$this->p_mode}"; + } - // Assign the module path for re-usage - $this->module->module_path = $module_path . '/'; + // Add url_extra parameter to u_action url + if (!empty($this->module_ary) && $this->active_module !== false && $this->module_ary[$this->active_module_row_id]['url_extra']) + { + $this->module->u_action .= $this->module_ary[$this->active_module_row_id]['url_extra']; + } - // Execute the main method for the new instance, we send the module id and mode as parameters - // Users are able to call the main method after this function to be able to assign additional parameters manually - if ($execute_module) - { - $this->module->main($this->p_name, $this->p_mode); - } + // Assign the module path for re-usage + $this->module->module_path = $module_path . '/'; - return; + // Execute the main method for the new instance, we send the module id and mode as parameters + // Users are able to call the main method after this function to be able to assign additional parameters manually + if ($execute_module) + { + $this->module->main($this->p_name, $this->p_mode); } } From ade496e0f7da84fb3035a811930792648a8ac442 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 22 Aug 2011 03:20:59 -0400 Subject: [PATCH 13/67] [feature/extension-manager] Fix whitespace in acp_modules PHPBB3-10323 --- phpBB/includes/acp/acp_modules.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php index 2a533056b4..367eea8e80 100644 --- a/phpBB/includes/acp/acp_modules.php +++ b/phpBB/includes/acp/acp_modules.php @@ -111,7 +111,7 @@ class acp_modules } break; - + case 'enable': case 'disable': if (!$module_id) @@ -170,7 +170,7 @@ class acp_modules add_log('admin', 'LOG_MODULE_' . strtoupper($action), $this->lang_name($row['module_langname']), $move_module_name); $this->remove_cache_file(); } - + break; case 'quickadd': @@ -207,7 +207,7 @@ class acp_modules if (!sizeof($errors)) { $this->remove_cache_file(); - + trigger_error($user->lang['MODULE_ADDED'] . adm_back_link($this->u_action . '&parent_id=' . $this->parent_id)); } } @@ -231,7 +231,7 @@ class acp_modules { trigger_error($user->lang['NO_MODULE_ID'] . adm_back_link($this->u_action . '&parent_id=' . $this->parent_id), E_USER_WARNING); } - + $module_row = $this->get_module_row($module_id); // no break @@ -250,7 +250,7 @@ class acp_modules 'module_auth' => '', ); } - + $module_data = array(); $module_data['module_basename'] = request_var('module_basename', (string) $module_row['module_basename']); @@ -295,7 +295,7 @@ class acp_modules if (!sizeof($errors)) { $this->remove_cache_file(); - + trigger_error((($action == 'add') ? $user->lang['MODULE_ADDED'] : $user->lang['MODULE_EDITED']) . adm_back_link($this->u_action . '&parent_id=' . $this->parent_id)); } } @@ -327,7 +327,7 @@ class acp_modules { $s_mode_options .= ''; } - + $template->assign_block_vars('m_names.modes', array( 'OPTION' => $m_mode, 'VALUE' => $this->lang_name($m_values['title']), @@ -336,7 +336,7 @@ class acp_modules ); } } - + $s_cat_option = ''; $template->assign_vars(array_merge(array( @@ -349,7 +349,7 @@ class acp_modules 'U_EDIT_ACTION' => $this->u_action . '&parent_id=' . $this->parent_id, 'L_TITLE' => $user->lang[strtoupper($action) . '_MODULE'], - + 'MODULENAME' => $this->lang_name($module_data['module_langname']), 'ACTION' => $action, 'MODULE_ID' => $module_id, @@ -516,7 +516,7 @@ class acp_modules $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); - + if (!$row) { trigger_error($user->lang['NO_MODULE'] . adm_back_link($this->u_action . '&parent_id=' . $this->parent_id), E_USER_WARNING); @@ -524,14 +524,14 @@ class acp_modules return $row; } - + /** * Get available module information from module files */ function get_module_infos($module = '', $module_class = false) { global $phpbb_root_path, $phpEx; - + $module_class = ($module_class === false) ? $this->module_class : $module_class; $directory = $phpbb_root_path . 'includes/' . $module_class . '/info/'; @@ -742,7 +742,7 @@ class acp_modules // Sanitise for future path use, it's escaped as appropriate for queries $p_class = str_replace(array('.', '/', '\\'), '', basename($this->module_class)); - + $cache->destroy('_modules_' . $p_class); // Additionally remove sql cache From d5a5cdd0d712ff7997f98659525ab98ee45fbe1f Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 22 Aug 2011 03:39:07 -0400 Subject: [PATCH 14/67] [feature/extension-manager] Avoid unecessary loading of acp classes PHPBB3-10323 --- phpBB/includes/acp/acp_modules.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php index 367eea8e80..a4e140ecfe 100644 --- a/phpBB/includes/acp/acp_modules.php +++ b/phpBB/includes/acp/acp_modules.php @@ -554,11 +554,13 @@ class acp_modules foreach ($modules as $module) { + $info_class = preg_replace('/_module$/', '_info', $module); + // If the class does not exist it might be following the old // format. phpbb_acp_info_acp_foo needs to be turned into // acp_foo_info and the respective file has to be included // manually because it does not support auto loading - if (!class_exists($module)) + if (!class_exists($info_class)) { $info_class = str_replace("phpbb_{$module_class}_info_", '', $module) . '_info'; if (file_exists($directory . $info_class . '.' . $phpEx)) @@ -566,10 +568,6 @@ class acp_modules include($directory . $info_class . '.' . $phpEx); } } - else - { - $info_class = preg_replace('/_module$/', '_info', $module); - } if (class_exists($info_class)) { @@ -586,7 +584,9 @@ class acp_modules } else { - if (!class_exists($module)) + $info_class = preg_replace('/_module$/', '_info', $module); + + if (!class_exists($info_class)) { if (file_exists($directory . $module . '.' . $phpEx)) { @@ -594,10 +594,6 @@ class acp_modules } $info_class = $module . '_info'; } - else - { - $info_class = preg_replace('/_module$/', '_info', $module); - } // Get module title tag if (class_exists($info_class)) From 61df8a87d19c062f5284f06047c1a9093c664cdf Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 22 Aug 2011 03:51:03 -0400 Subject: [PATCH 15/67] [feature/extension-manager] Allow extensions to define captcha plugins. The base class for captcha plugins has been renamed, but the old name continues to exist as an empty subclass of it for backwards compatability. PHPBB3-10323 --- phpBB/includes/acp/acp_captcha.php | 4 +- phpBB/includes/captcha/captcha_factory.php | 45 ++++++++++--------- .../captcha/plugins/captcha_abstract.php | 9 +++- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/phpBB/includes/acp/acp_captcha.php b/phpBB/includes/acp/acp_captcha.php index bef8ae0ea9..f051781547 100644 --- a/phpBB/includes/acp/acp_captcha.php +++ b/phpBB/includes/acp/acp_captcha.php @@ -104,13 +104,13 @@ class acp_captcha foreach ($captchas['available'] as $value => $title) { $current = ($selected !== false && $value == $selected) ? ' selected="selected"' : ''; - $captcha_select .= ''; + $captcha_select .= ''; } foreach ($captchas['unavailable'] as $value => $title) { $current = ($selected !== false && $value == $selected) ? ' selected="selected"' : ''; - $captcha_select .= ''; + $captcha_select .= ''; } $demo_captcha = phpbb_captcha_factory::get_instance($selected); diff --git a/phpBB/includes/captcha/captcha_factory.php b/phpBB/includes/captcha/captcha_factory.php index c2ec8c5bda..2779a23d34 100644 --- a/phpBB/includes/captcha/captcha_factory.php +++ b/phpBB/includes/captcha/captcha_factory.php @@ -59,38 +59,39 @@ class phpbb_captcha_factory */ function get_captcha_types() { - global $phpbb_root_path, $phpEx; + global $phpbb_root_path, $phpEx, $phpbb_extension_manager; $captchas = array( 'available' => array(), 'unavailable' => array(), ); - $dp = @opendir($phpbb_root_path . 'includes/captcha/plugins'); + $finder = $phpbb_extension_manager->get_finder(); + $captcha_plugin_classes = $finder + ->directory('/captcha') + ->suffix('_plugin') + ->default_path('includes/captcha/plugins/') + ->default_directory('') + ->get_classes(); - if ($dp) + foreach ($captcha_plugin_classes as $class) { - while (($file = readdir($dp)) !== false) + // check if this class needs to be loaded in legacy mode + $old_class = preg_replace('/^phpbb_captcha_plugins_/', '', $class); + if (file_exists($phpbb_root_path . "includes/captcha/plugins/$old_class.$phpEx") && !class_exists($old_class)) { - if ((preg_match('#_plugin\.' . $phpEx . '$#', $file))) - { - $name = preg_replace('#^(.*?)_plugin\.' . $phpEx . '$#', '\1', $file); - if (!class_exists($name)) - { - include($phpbb_root_path . "includes/captcha/plugins/$file"); - } - - if (call_user_func(array($name, 'is_available'))) - { - $captchas['available'][$name] = call_user_func(array($name, 'get_name')); - } - else - { - $captchas['unavailable'][$name] = call_user_func(array($name, 'get_name')); - } - } + include($phpbb_root_path . "includes/captcha/plugins/$old_class.$phpEx"); + $class = preg_replace('/_plugin$/', '', $old_class); + } + + if (call_user_func(array($class, 'is_available'))) + { + $captchas['available'][$class] = call_user_func(array($class, 'get_name')); + } + else + { + $captchas['unavailable'][$class] = call_user_func(array($class, 'get_name')); } - closedir($dp); } return $captchas; diff --git a/phpBB/includes/captcha/plugins/captcha_abstract.php b/phpBB/includes/captcha/plugins/captcha_abstract.php index aea39b3123..07a0ea1279 100644 --- a/phpBB/includes/captcha/plugins/captcha_abstract.php +++ b/phpBB/includes/captcha/plugins/captcha_abstract.php @@ -22,7 +22,7 @@ if (!defined('IN_PHPBB')) * * @package VC */ -class phpbb_default_captcha +class phpbb_captcha_plugins_captcha_abstract { var $confirm_id; var $confirm_code; @@ -364,3 +364,10 @@ class phpbb_default_captcha } } + +/** +* Old class name for legacy use. The new class name is auto loadable. +*/ +class phpbb_default_captcha extends phpbb_captcha_plugins_captcha_abstract +{ +} From 897063d3e269a7c11ef6d6602abc37ec30266a72 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 14:46:38 -0400 Subject: [PATCH 16/67] [feature/extension-manager] Add missing sql_freeresult call PHPBB3-10323 --- phpBB/includes/extension/manager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index 736c706e77..d167bc6847 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -63,6 +63,7 @@ class phpbb_extension_manager $result = $this->db->sql_query($sql); $extensions = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); $this->extensions = array(); foreach ($extensions as $extension) From c7a986eccdac183cc81b3da486092f4ab82109ba Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 17:17:40 -0400 Subject: [PATCH 17/67] [feature/extension-manager] Use an incremental process for enable and purge The enable or purge operation of an extension could take a long time if an expensive operation needs to be executed on a large set of data. To allow this to succeed from a web interface with max_execution_time set in the webserver's php configuration, subsequent requests must continue the operation started earlier. So individual enable and purge implementations must be able to spread their work across multiple steps. PHPBB3-10323 --- phpBB/develop/create_schema_files.php | 1 + phpBB/includes/extension/base.php | 22 +++++- phpBB/includes/extension/interface.php | 39 ++++++++++- phpBB/includes/extension/manager.php | 85 +++++++++++++++++++---- phpBB/install/database_update.php | 1 + phpBB/install/schemas/firebird_schema.sql | 3 +- phpBB/install/schemas/mssql_schema.sql | 3 +- phpBB/install/schemas/mysql_40_schema.sql | 1 + phpBB/install/schemas/mysql_41_schema.sql | 1 + phpBB/install/schemas/oracle_schema.sql | 1 + phpBB/install/schemas/postgres_schema.sql | 3 +- phpBB/install/schemas/sqlite_schema.sql | 3 +- tests/extension/manager_test.php | 10 +++ 13 files changed, 151 insertions(+), 22 deletions(-) diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index bc1e28572d..1828719570 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -1035,6 +1035,7 @@ function get_schema_struct() 'COLUMNS' => array( 'ext_name' => array('VCHAR', ''), 'ext_active' => array('BOOL', 0), + 'ext_state' => array('TEXT', ''), ), 'KEYS' => array( 'ext_name' => array('UNIQUE', 'ext_name'), diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php index 0e6c89491d..8228364d44 100644 --- a/phpBB/includes/extension/base.php +++ b/phpBB/includes/extension/base.php @@ -16,20 +16,38 @@ if (!defined('IN_PHPBB')) } /** +* A base class for extensions without custom enable/disbale/purge code. * * @package extension */ class phpbb_extension_base implements phpbb_extension_interface { - public function enable() + /** + * Single enable step that does nothing + * + * @return false Indicates no further steps are required + */ + public function enable_step($old_state) { + return false; } + /** + * Empty disable method + * + * @return null + */ public function disable() { } - public function purge() + /** + * Single purge step that does nothing + * + * @return false Indicates no further steps are required + */ + public function purge_step($old_state) { + return false; } } diff --git a/phpBB/includes/extension/interface.php b/phpBB/includes/extension/interface.php index 40a5a066a3..7d0ecd72c7 100644 --- a/phpBB/includes/extension/interface.php +++ b/phpBB/includes/extension/interface.php @@ -16,12 +16,47 @@ if (!defined('IN_PHPBB')) } /** +* The interface extension meta classes have to implement to run custom code +* on enable/disable/purge. * * @package extension */ interface phpbb_extension_interface { - public function enable(); + /** + * enable_step is executed on enabling an extension until it returns false. + * + * Calls to this function can be made in subsequent requests, when the + * function is invoked through a webserver with a too low max_execution_time. + * + * @param mixed $old_state The return value of the previous call + * of this method, or false on the first call + * @return mixed Returns false after last step, otherwise + * temporary state which is passed as an + * argument to the next step + */ + public function enable_step($old_state); + + /** + * Disables the extension. + * + * Must be a quick operation, that finishes within max_execution_time. + * + * @return null + */ public function disable(); - public function purge(); + + /** + * purge_step is executed on purging an extension until it returns false. + * + * Calls to this function can be made in subsequent requests, when the + * function is invoked through a webserver with a too low max_execution_time. + * + * @param mixed $old_state The return value of the previous call + * of this method, or false on the first call + * @return mixed Returns false after last step, otherwise + * temporary state which is passed as an + * argument to the next step + */ + public function purge_step($old_state); } diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index d167bc6847..a1863040d0 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -109,28 +109,34 @@ class phpbb_extension_manager } /** - * Enables an extension + * Runs a step of the extension enabling process. * - * Calls the enable method on the extension's meta class to allow it to - * make database changes and execute other initialisation code. + * Allows the exentension to enable in a long running script that works + * in multiple steps across requests. State is kept for the extension + * in the extensions table. * - * @param string $name The extension's name - * @return null + * @param string $name The extension's name + * @return bool Whether another run of enable_step is required */ - public function enable($name) + public function enable_step($name) { // ignore extensions that are already enabled if (isset($this->extensions[$name]) && $this->extensions[$name]['ext_active']) { - return; + return false; } + $old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false; + $extension = $this->get_extension($name); - $extension->enable(); + $state = $extension->enable_step($old_state); + + $active = ($state === false); $extension_data = array( - 'ext_name' => $name, - 'ext_active' => true, + 'ext_name' => $name, + 'ext_active' => $active, + 'ext_state' => serialize($state), ); $this->extensions[$name] = $extension_data; @@ -148,6 +154,22 @@ class phpbb_extension_manager ' . $this->db->sql_build_array('INSERT', $extension_data); $this->db->sql_query($sql); } + + return !$active; + } + + /** + * Enables an extension + * + * This method completely enables an extension. But it could be long running + * so never call this in a script that has a max_execution time. + * + * @param string $name The extension's name + * @return null + */ + public function enable($name) + { + while ($this->enable_step($name)); } /** @@ -172,9 +194,10 @@ class phpbb_extension_manager $extension_data = array( 'ext_active' => false, + 'ext_state' => serialize(false), ); $this->extensions[$name]['ext_active'] = false; - ksort($this->extensions); + $this->extensions[$name]['ext_state'] = serialize(false); $sql = 'UPDATE ' . $this->extension_table . ' SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . " @@ -191,12 +214,12 @@ class phpbb_extension_manager * @param string $name The extension's name * @return null */ - public function purge($name) + public function purge_step($name) { // ignore extensions that do not exist if (!isset($this->extensions[$name])) { - return; + return false; } // disable first if necessary @@ -205,14 +228,48 @@ class phpbb_extension_manager $this->disable($name); } + $old_state = unserialize($this->extensions[$name]['ext_state']); + $extension = $this->get_extension($name); - $extension->purge(); + $state = $extension->purge_step($old_state); + + // continue until the state is false + if ($state !== false) + { + $extension_data = array( + 'ext_state' => serialize($state), + ); + $this->extensions[$name]['ext_state'] = serialize($state); + + $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); + + return true; + } unset($this->extensions[$name]); $sql = 'DELETE FROM ' . $this->extension_table . " WHERE ext_name = '" . $this->db->sql_escape($name) . "'"; $this->db->sql_query($sql); + + return false; + } + + /** + * Purge an extension + * + * Purges an extension completely at once. This process could run for a while + * so never call this in a script that has a max_execution time. + * + * @param string $name The extension's name + * @return null + */ + public function purge($name) + { + while ($this->purge_step($name)); } /** diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 45de19c918..9a7231040b 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -1060,6 +1060,7 @@ function database_update_info() 'COLUMNS' => array( 'ext_name' => array('VCHAR', ''), 'ext_active' => array('BOOL', 0), + 'ext_state' => array('TEXT', ''), ), 'KEYS' => array( 'ext_name' => array('UNIQUE', 'ext_name'), diff --git a/phpBB/install/schemas/firebird_schema.sql b/phpBB/install/schemas/firebird_schema.sql index 4952b6bbdf..9bebf11c4b 100644 --- a/phpBB/install/schemas/firebird_schema.sql +++ b/phpBB/install/schemas/firebird_schema.sql @@ -284,7 +284,8 @@ END;; # Table: 'phpbb_ext' CREATE TABLE phpbb_ext ( ext_name VARCHAR(255) CHARACTER SET NONE DEFAULT '' NOT NULL, - ext_active INTEGER DEFAULT 0 NOT NULL + ext_active INTEGER DEFAULT 0 NOT NULL, + ext_state BLOB SUB_TYPE TEXT CHARACTER SET NONE DEFAULT '' NOT NULL );; CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext(ext_name);; diff --git a/phpBB/install/schemas/mssql_schema.sql b/phpBB/install/schemas/mssql_schema.sql index 76f3fc6491..bf137b89f4 100644 --- a/phpBB/install/schemas/mssql_schema.sql +++ b/phpBB/install/schemas/mssql_schema.sql @@ -363,7 +363,8 @@ GO */ CREATE TABLE [phpbb_ext] ( [ext_name] [varchar] (255) DEFAULT ('') NOT NULL , - [ext_active] [int] DEFAULT (0) NOT NULL + [ext_active] [int] DEFAULT (0) NOT NULL , + [ext_state] [varchar] (8000) DEFAULT ('') NOT NULL ) ON [PRIMARY] GO diff --git a/phpBB/install/schemas/mysql_40_schema.sql b/phpBB/install/schemas/mysql_40_schema.sql index a1b282418d..54504beb1c 100644 --- a/phpBB/install/schemas/mysql_40_schema.sql +++ b/phpBB/install/schemas/mysql_40_schema.sql @@ -195,6 +195,7 @@ CREATE TABLE phpbb_drafts ( CREATE TABLE phpbb_ext ( ext_name varbinary(255) DEFAULT '' NOT NULL, ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL, + ext_state blob NOT NULL, UNIQUE ext_name (ext_name) ); diff --git a/phpBB/install/schemas/mysql_41_schema.sql b/phpBB/install/schemas/mysql_41_schema.sql index 2f2de17dcb..139833b7b9 100644 --- a/phpBB/install/schemas/mysql_41_schema.sql +++ b/phpBB/install/schemas/mysql_41_schema.sql @@ -195,6 +195,7 @@ CREATE TABLE phpbb_drafts ( CREATE TABLE phpbb_ext ( ext_name varchar(255) DEFAULT '' NOT NULL, ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL, + ext_state text NOT NULL, UNIQUE ext_name (ext_name) ) CHARACTER SET `utf8` COLLATE `utf8_bin`; diff --git a/phpBB/install/schemas/oracle_schema.sql b/phpBB/install/schemas/oracle_schema.sql index f0a958d2f6..2f958b835b 100644 --- a/phpBB/install/schemas/oracle_schema.sql +++ b/phpBB/install/schemas/oracle_schema.sql @@ -413,6 +413,7 @@ END; CREATE TABLE phpbb_ext ( ext_name varchar2(255) DEFAULT '' , ext_active number(1) DEFAULT '0' NOT NULL, + ext_state clob DEFAULT '' , CONSTRAINT u_phpbb_ext_name UNIQUE (ext_name) ) / diff --git a/phpBB/install/schemas/postgres_schema.sql b/phpBB/install/schemas/postgres_schema.sql index 956167ff39..86ee044913 100644 --- a/phpBB/install/schemas/postgres_schema.sql +++ b/phpBB/install/schemas/postgres_schema.sql @@ -317,7 +317,8 @@ CREATE INDEX phpbb_drafts_save_time ON phpbb_drafts (save_time); */ CREATE TABLE phpbb_ext ( ext_name varchar(255) DEFAULT '' NOT NULL, - ext_active INT2 DEFAULT '0' NOT NULL CHECK (ext_active >= 0) + ext_active INT2 DEFAULT '0' NOT NULL CHECK (ext_active >= 0), + ext_state varchar(8000) DEFAULT '' NOT NULL ); CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name); diff --git a/phpBB/install/schemas/sqlite_schema.sql b/phpBB/install/schemas/sqlite_schema.sql index 223dbc3c02..94e192fc0c 100644 --- a/phpBB/install/schemas/sqlite_schema.sql +++ b/phpBB/install/schemas/sqlite_schema.sql @@ -189,7 +189,8 @@ CREATE INDEX phpbb_drafts_save_time ON phpbb_drafts (save_time); # Table: 'phpbb_ext' CREATE TABLE phpbb_ext ( ext_name varchar(255) NOT NULL DEFAULT '', - ext_active INTEGER UNSIGNED NOT NULL DEFAULT '0' + ext_active INTEGER UNSIGNED NOT NULL DEFAULT '0', + ext_state text(65535) NOT NULL DEFAULT '' ); CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name); diff --git a/tests/extension/manager_test.php b/tests/extension/manager_test.php index 2035264559..70c3543a69 100644 --- a/tests/extension/manager_test.php +++ b/tests/extension/manager_test.php @@ -8,6 +8,8 @@ */ require_once dirname(__FILE__) . '/../mock/cache.php'; +require_once dirname(__FILE__) . '/ext/bar/bar.php'; +require_once dirname(__FILE__) . '/ext/moo/moo.php'; class phpbb_extension_manager_test extends phpbb_database_test_case { @@ -49,10 +51,14 @@ class phpbb_extension_manager_test extends phpbb_database_test_case public function test_enable() { + phpbb_ext_bar::$state = 0; + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); $this->extension_manager->enable('bar'); $this->assertEquals(array('bar', 'foo'), array_keys($this->extension_manager->all_enabled())); $this->assertEquals(array('bar', 'foo', 'moo'), array_keys($this->extension_manager->all_configured())); + + $this->assertEquals(4, phpbb_ext_bar::$state); } public function test_disable() @@ -65,10 +71,14 @@ class phpbb_extension_manager_test extends phpbb_database_test_case public function test_purge() { + phpbb_ext_moo::$purged = false; + $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); $this->assertEquals(array('foo', 'moo'), array_keys($this->extension_manager->all_configured())); $this->extension_manager->purge('moo'); $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled())); $this->assertEquals(array('foo'), array_keys($this->extension_manager->all_configured())); + + $this->assertTrue(phpbb_ext_moo::$purged); } } From 24ddef2230b63dd752e8b3bb33ae6b651f9e8b5e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 18:10:50 -0400 Subject: [PATCH 18/67] [feature/extension-manager] Remove 5.2 incompatible \ in front of SPL classname PHPBB-10323 --- phpBB/includes/cron/provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/includes/cron/provider.php b/phpBB/includes/cron/provider.php index 9936da3f55..ee0f93a308 100644 --- a/phpBB/includes/cron/provider.php +++ b/phpBB/includes/cron/provider.php @@ -22,7 +22,7 @@ if (!defined('IN_PHPBB')) * * @package phpBB3 */ -class phpbb_cron_provider implements \IteratorAggregate +class phpbb_cron_provider implements IteratorAggregate { /** * Array holding all found task class names. From 7435f344e20ae4dce875b46746f46ddc874e41f3 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 18:16:57 -0400 Subject: [PATCH 19/67] [feature/extension-manager] Add docblocks for query members of extension finder PHPBB3-10323 --- phpBB/includes/extension/finder.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index fbc8a3aefb..b3cdbd6ab9 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -27,7 +27,16 @@ class phpbb_extension_finder protected $cache; protected $phpEx; + /** + * @var array An associative array, containing all search parameters set in + * methods + */ protected $query; + + /** + * @var array A map from md5 hashes of serialized queries to their + * previously retrieved results. + */ protected $cached_queries; /** From 34f11a1039745ee1de48f1818f781ae1ee2b85ac Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 18:24:15 -0400 Subject: [PATCH 20/67] [feature/extension-manager] Correct usage of false cache return value PHPBB3-10323 --- phpBB/includes/extension/finder.php | 5 +++++ phpBB/includes/extension/manager.php | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index b3cdbd6ab9..d25823ca43 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -283,6 +283,11 @@ class phpbb_extension_finder if ($cache && $this->cache) { + if ($this->cached_queries === false) + { + $this->cached_queries = array(); + } + $this->cached_queries[$query] = $files; $this->cache->put('_extension_finder', $this->cached_queries); } diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index a1863040d0..e1e7571573 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -45,7 +45,9 @@ class phpbb_extension_manager $this->phpEx = $phpEx; $this->extension_table = $extension_table; - if (false === ($this->extensions = $this->cache->get('_extensions'))) + $this->extensions = $this->cache->get('_extensions'); + + if ($this->extensions === false) { $this->load_extensions(); } From 64827a6623c9a916d41c2ef5bf2c2092a3008722 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 18:43:45 -0400 Subject: [PATCH 21/67] [feature/extension-manager] Test creation of new extension finder cache PHPBB3-10323 --- phpBB/includes/extension/finder.php | 5 ----- tests/extension/finder_test.php | 31 ++++++++++++++++++++++++++++- tests/mock/cache.php | 12 +++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index d25823ca43..b3cdbd6ab9 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -283,11 +283,6 @@ class phpbb_extension_finder if ($cache && $this->cache) { - if ($this->cached_queries === false) - { - $this->cached_queries = array(); - } - $this->cached_queries[$query] = $files; $this->cache->put('_extension_finder', $this->cached_queries); } diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php index 4acfe53937..fb2faaf5c0 100644 --- a/tests/extension/finder_test.php +++ b/tests/extension/finder_test.php @@ -122,6 +122,36 @@ class phpbb_extension_finder_test extends phpbb_test_case ); } + public function test_get_classes_create_cache() + { + $cache = new phpbb_mock_cache; + $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', $cache); + $files = $finder->suffix('_class.php')->get_files(); + + sort($files); + + $expected_files = array( + 'ext/bar/my/hidden_class.php', + 'ext/foo/a_class.php', + 'ext/foo/b_class.php', + ); + + $query = array( + 'default_path' => false, + 'default_suffix' => '_class.php', + 'default_prefix' => false, + 'default_directory' => false, + 'suffix' => '_class.php', + 'prefix' => false, + 'directory' => false, + ); + + $this->assertEquals($expected_files, $files); + $cache->checkAssociativeVar($this, '_extension_finder', array( + md5(serialize($query)) => $expected_files, + )); + } + public function test_cached_get_files() { $query = array( @@ -134,7 +164,6 @@ class phpbb_extension_finder_test extends phpbb_test_case 'directory' => false, ); - $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', new phpbb_mock_cache(array( '_extension_finder' => array( md5(serialize($query)) => array('file_name'), diff --git a/tests/mock/cache.php b/tests/mock/cache.php index 989180c256..b745123801 100644 --- a/tests/mock/cache.php +++ b/tests/mock/cache.php @@ -59,6 +59,18 @@ class phpbb_mock_cache implements phpbb_cache_driver_interface $test->assertEquals($data, $this->data[$var_name]); } + public function checkAssociativeVar(PHPUnit_Framework_Assert $test, $var_name, $data) + { + $test->assertTrue(isset($this->data[$var_name])); + + foreach ($this->data[$var_name] as &$content) + { + sort($content); + } + + $test->assertEquals($data, $this->data[$var_name]); + } + public function checkVarUnset(PHPUnit_Framework_Assert $test, $var_name) { $test->assertFalse(isset($this->data[$var_name])); From bd1366d62d018c6b71ea24b1f9915d89d4a240a5 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 18:44:43 -0400 Subject: [PATCH 22/67] [feature/extension-manager] Use _ext for cache - avoids conflict with file ext PHPBB3-10323 --- phpBB/includes/extension/finder.php | 4 ++-- phpBB/includes/extension/manager.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index b3cdbd6ab9..7ba477582c 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -66,7 +66,7 @@ class phpbb_extension_finder 'directory' => false, ); - $this->cached_queries = ($this->cache) ? $this->cache->get('_extension_finder') : false; + $this->cached_queries = ($this->cache) ? $this->cache->get('_ext_finder') : false; } /** @@ -284,7 +284,7 @@ class phpbb_extension_finder if ($cache && $this->cache) { $this->cached_queries[$query] = $files; - $this->cache->put('_extension_finder', $this->cached_queries); + $this->cache->put('_ext_finder', $this->cached_queries); } return $files; diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index e1e7571573..45250e623a 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -45,7 +45,7 @@ class phpbb_extension_manager $this->phpEx = $phpEx; $this->extension_table = $extension_table; - $this->extensions = $this->cache->get('_extensions'); + $this->extensions = $this->cache->get('_ext'); if ($this->extensions === false) { @@ -75,7 +75,7 @@ class phpbb_extension_manager } ksort($this->extensions); - $this->cache->put('_extensions', $this->extensions); + $this->cache->put('_ext', $this->extensions); } /** From 739e9eb58e7e9b899955b714aa3fc8bbe6a30ebc Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 18:57:21 -0400 Subject: [PATCH 23/67] [feature/extension-manager] Make the cache variable name for extensions dynamic Allows multiple instances to use cache simultaneously. PHPBB3-10323 --- phpBB/includes/extension/finder.php | 10 +++++++--- phpBB/includes/extension/manager.php | 11 +++++++---- tests/extension/finder_test.php | 6 +++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index 7ba477582c..e94a94c733 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -26,6 +26,7 @@ class phpbb_extension_finder protected $phpbb_root_path; protected $cache; protected $phpEx; + protected $cache_name; /** * @var array An associative array, containing all search parameters set in @@ -48,13 +49,16 @@ class phpbb_extension_finder * @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 + * @param string $cache_name The name of the cache variable, defaults to + * _ext_finder */ - public function __construct(phpbb_extension_manager $extension_manager, $phpbb_root_path = '', phpbb_cache_driver_interface $cache = null, $phpEx = '.php') + public function __construct(phpbb_extension_manager $extension_manager, $phpbb_root_path = '', phpbb_cache_driver_interface $cache = null, $phpEx = '.php', $cache_name = '_ext_finder') { $this->extension_manager = $extension_manager; $this->phpbb_root_path = $phpbb_root_path; $this->cache = $cache; $this->phpEx = $phpEx; + $this->cache_name = $cache_name; $this->query = array( 'default_path' => false, @@ -66,7 +70,7 @@ class phpbb_extension_finder 'directory' => false, ); - $this->cached_queries = ($this->cache) ? $this->cache->get('_ext_finder') : false; + $this->cached_queries = ($this->cache) ? $this->cache->get($this->cache_name) : false; } /** @@ -284,7 +288,7 @@ class phpbb_extension_finder if ($cache && $this->cache) { $this->cached_queries[$query] = $files; - $this->cache->put('_ext_finder', $this->cached_queries); + $this->cache->put($this->cache_name, $this->cached_queries); } return $files; diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index 45250e623a..443eaf011b 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -27,6 +27,7 @@ class phpbb_extension_manager protected $extensions; protected $extension_table; protected $phpbb_root_path; + protected $cache_name; /** * Creates a manager and loads information from database @@ -36,16 +37,18 @@ class phpbb_extension_manager * @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 + * @param string $cache_name The name of the cache variable, defaults to _ext */ - public function __construct(dbal $db, $extension_table, $phpbb_root_path, $phpEx = '.php', phpbb_cache_driver_interface $cache = null) + public function __construct(dbal $db, $extension_table, $phpbb_root_path, $phpEx = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext') { $this->phpbb_root_path = $phpbb_root_path; $this->db = $db; $this->cache = $cache; $this->phpEx = $phpEx; $this->extension_table = $extension_table; + $this->cache_name = $cache_name; - $this->extensions = $this->cache->get('_ext'); + $this->extensions = $this->cache->get($this->cache_name); if ($this->extensions === false) { @@ -75,7 +78,7 @@ class phpbb_extension_manager } ksort($this->extensions); - $this->cache->put('_ext', $this->extensions); + $this->cache->put($this->cache_name, $this->extensions); } /** @@ -356,6 +359,6 @@ class phpbb_extension_manager */ public function get_finder() { - return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->phpEx); + return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->phpEx, $this->cache_name . '_finder'); } } diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php index fb2faaf5c0..cae11a5bfa 100644 --- a/tests/extension/finder_test.php +++ b/tests/extension/finder_test.php @@ -125,7 +125,7 @@ class phpbb_extension_finder_test extends phpbb_test_case public function test_get_classes_create_cache() { $cache = new phpbb_mock_cache; - $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', $cache); + $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', $cache, '.php', '_custom_cache_name'); $files = $finder->suffix('_class.php')->get_files(); sort($files); @@ -147,7 +147,7 @@ class phpbb_extension_finder_test extends phpbb_test_case ); $this->assertEquals($expected_files, $files); - $cache->checkAssociativeVar($this, '_extension_finder', array( + $cache->checkAssociativeVar($this, '_custom_cache_name', array( md5(serialize($query)) => $expected_files, )); } @@ -165,7 +165,7 @@ class phpbb_extension_finder_test extends phpbb_test_case ); $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', new phpbb_mock_cache(array( - '_extension_finder' => array( + '_ext_finder' => array( md5(serialize($query)) => array('file_name'), ), ))); From 48391d2dde8f40e3c7e7d4a41b8ff1c32508ffc1 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 20:05:46 -0400 Subject: [PATCH 24/67] [feature/extension-manager] Create an extension manager on update and install It's required when adding modules PHPBB3-10323 --- phpBB/install/database_update.php | 8 +++++++- phpBB/install/install_install.php | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 9a7231040b..2635396af0 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -678,7 +678,13 @@ function _write_result($no_updates, $errored, $error_ary) function _add_modules($modules_to_install) { - global $phpbb_root_path, $phpEx, $db; + global $phpbb_root_path, $phpEx, $db, $phpbb_extension_manager; + + // modules require an extension manager + if (empty($phpbb_extension_manager)) + { + $phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx"); + } include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx); diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index f8c54678bf..c6b315ae6c 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1483,7 +1483,13 @@ class install_install extends module */ function add_modules($mode, $sub) { - global $db, $lang, $phpbb_root_path, $phpEx; + global $db, $lang, $phpbb_root_path, $phpEx, $phpbb_extension_manager; + + // modules require an extension manager + if (empty($phpbb_extension_manager)) + { + $phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx"); + } include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx); From 018a8359974273b65a3e6bd8aeca75396fd36a5b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 20:06:17 -0400 Subject: [PATCH 25/67] [feature/extension-manager] The default fulltext native backend was renamed This was done to make it autoloadable. PHPBB3-10323 --- phpBB/install/install_install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index c6b315ae6c..5ea3525edd 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1465,7 +1465,7 @@ class install_install extends module set_config_count(null, null, null, $config); $error = false; - $search = new fulltext_native($error); + $search = new phpbb_search_fulltext_native($error); $sql = 'SELECT post_id, post_subject, post_text, poster_id, forum_id FROM ' . POSTS_TABLE; From c785ef7aa7953c5e533e48b11ef13d6b1f344813 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 20:14:23 -0400 Subject: [PATCH 26/67] [feature/extension-manager] Make sure the extension manager works without cache Includes a test for manager without a cache PHPBB3-10323 --- phpBB/includes/extension/manager.php | 8 ++++++-- tests/extension/manager_test.php | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index 443eaf011b..a6c8ebb3d0 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -48,7 +48,7 @@ class phpbb_extension_manager $this->extension_table = $extension_table; $this->cache_name = $cache_name; - $this->extensions = $this->cache->get($this->cache_name); + $this->extensions = ($this->cache) ? $this->cache->get($this->cache_name) : false; if ($this->extensions === false) { @@ -78,7 +78,11 @@ class phpbb_extension_manager } ksort($this->extensions); - $this->cache->put($this->cache_name, $this->extensions); + + if ($this->cache) + { + $this->cache->put($this->cache_name, $this->extensions); + } } /** diff --git a/tests/extension/manager_test.php b/tests/extension/manager_test.php index 70c3543a69..52be88330a 100644 --- a/tests/extension/manager_test.php +++ b/tests/extension/manager_test.php @@ -81,4 +81,17 @@ class phpbb_extension_manager_test extends phpbb_database_test_case $this->assertTrue(phpbb_ext_moo::$purged); } + + public function test_enabled_no_cache() + { + $extension_manager = new phpbb_extension_manager( + $this->new_dbal(), + 'phpbb_ext', + dirname(__FILE__) . '/', + '.php' + ); + + $this->assertEquals(array('foo'), array_keys($extension_manager->all_enabled())); + } + } From fe4b8818ec1f448d5625534e5027cfbc1177ab9a Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 20:54:42 -0400 Subject: [PATCH 27/67] [feature/extension-manager] Always store the full class name as module basename The updater swaps out all basenames. PHPBB3-10323 --- phpBB/includes/functions_module.php | 15 +++++++------ phpBB/install/database_update.php | 35 +++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php index 1a6b57794a..4d575a7e3a 100644 --- a/phpBB/includes/functions_module.php +++ b/phpBB/includes/functions_module.php @@ -441,18 +441,19 @@ class p_master } // new modules use the full class names, old ones are always called _, e.g. acp_board - if (!class_exists($this->p_name) && !class_exists("{$this->p_class}_$this->p_name")) + if (!class_exists($this->p_name)) { - if (!file_exists("$module_path/{$this->p_class}_$this->p_name.$phpEx")) + if (!file_exists("$module_path/{$this->p_name}.$phpEx")) { - trigger_error("Cannot find module $module_path/{$this->p_class}_$this->p_name.$phpEx", E_USER_ERROR); + + trigger_error("Cannot find module $module_path/{$this->p_name}.$phpEx", E_USER_ERROR); } - include("$module_path/{$this->p_class}_$this->p_name.$phpEx"); + include("$module_path/{$this->p_name}.$phpEx"); - if (!class_exists("{$this->p_class}_$this->p_name")) + if (!class_exists($this->p_name)) { - trigger_error("Module file $module_path/{$this->p_class}_$this->p_name.$phpEx does not contain correct class [{$this->p_class}_$this->p_name]", E_USER_ERROR); + trigger_error("Module file $module_path/{$this->p_name}.$phpEx does not contain correct class [{$this->p_name}]", E_USER_ERROR); } } @@ -462,7 +463,7 @@ class p_master } // Create a new instance of the desired module ... - $class_name = (class_exists($this->p_name)) ? $this->p_name : "{$this->p_class}_$this->p_name"; + $class_name = $this->p_name; $this->module = new $class_name($this); diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 2635396af0..221f6b1344 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -2115,6 +2115,37 @@ function change_database_data(&$no_updates, $version) // Changes from 3.1.0-dev to 3.1.0-A1 case '3.1.0-dev': + + // rename all module basenames to full classname + $sql = 'SELECT module_id, module_basename, module_class + FROM ' . MODULES_TABLE; + $result = $db->sql_query($sql); + + while ($row = $db->sql_fetchrow($result)) + { + $module_id = (int) $row['module_id']; + unset($row['module_id']); + + if (!empty($row['module_basename']) && !empty($row['module_class'])) + { + // all the class names start with class name or with phpbb_ for auto loading + if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 && + strpos($row['module_basename'], 'phpbb_') !== 0) + { + $row['module_basename'] = $row['module_class'] . '_' . $row['module_basename']; + + $sql_update = $db->sql_build_array('UPDATE', $row); + + $sql = 'UPDATE ' . MODULES_TABLE . ' + SET ' . $sql_update . ' + WHERE module_id = ' . $module_id; + _sql($sql, $errored, $error_ary); + } + } + } + + $db->sql_freeresult($result); + // try to guess the new auto loaded search class name // works for native and mysql fulltext set_config('search_type', 'phpbb_search_' . $config['search_type']); @@ -2159,14 +2190,14 @@ function change_database_data(&$no_updates, $version) // Install modules $modules_to_install = array( 'position' => array( - 'base' => 'groups', + 'base' => 'acp_groups', 'class' => 'acp', 'title' => 'ACP_GROUPS_POSITION', 'auth' => 'acl_a_group', 'cat' => 'ACP_GROUPS', ), 'manage' => array( - 'base' => 'attachments', + 'base' => 'acp_attachments', 'class' => 'acp', 'title' => 'ACP_MANAGE_ATTACHMENTS', 'auth' => 'acl_a_attach', From 0ea4de41711e1b4be601d01882ff52011cf9bf48 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 22:21:20 -0400 Subject: [PATCH 28/67] [feature/extension-manager] Add support for directories to the extension finder PHPBB3-10323 --- phpBB/includes/extension/finder.php | 51 +++++++++++++++++++++++++---- tests/extension/finder_test.php | 14 ++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index e94a94c733..bcdcc61d76 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -216,14 +216,38 @@ class phpbb_extension_finder return $classes; } + /** + * Finds all directories matching the configured options + * + * @param bool $cache Whether the result should be cached + * @return array An array of paths to found directories + */ + public function get_directories($cache = true) + { + return $this->find($cache, true); + } + /** * Finds all files matching the configured options. * * @param bool $cache Whether the result should be cached - * @return array An array of found class names + * @return array An array of paths to found files */ public function get_files($cache = true) { + return $this->find($cache, false); + } + + /** + * Finds all file system entries matching the configured options + * + * @param bool $cache Whether the result should be cached + * @param bool $is_dir Whether the found items should be directories + * @return array An array of paths to found items + */ + protected function find($cache = true, $is_dir = false) + { + $this->query['is_dir'] = $is_dir; $query = md5(serialize($this->query)); if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query])) @@ -265,18 +289,33 @@ class phpbb_extension_finder } // match only first directory if leading slash is given - $directory_pattern = ($directory && $directory[0] === '/') ? '#^' : '#' . DIRECTORY_SEPARATOR; - $directory_pattern .= preg_quote($directory . DIRECTORY_SEPARATOR, '#') . '#'; + if ($directory === '/') + { + $directory_pattern = '^' . preg_quote(DIRECTORY_SEPARATOR, '#'); + } + else if ($directory && $directory[0] === '/') + { + $directory_pattern = '^' . preg_quote($directory . DIRECTORY_SEPARATOR, '#'); + } + else + { + $directory_pattern = preg_quote(DIRECTORY_SEPARATOR . $directory . DIRECTORY_SEPARATOR, '#'); + } + $directory_pattern = '#' . $directory_pattern . '#'; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)); foreach ($iterator as $file_info) { - if (!$file_info->isDir()) + if ($file_info->isDir() == $is_dir) { - $relative_path = $iterator->getInnerIterator()->getSubPathname(); + $relative_path = ($is_dir) ? $iterator->getInnerIterator()->getSubPath() . DIRECTORY_SEPARATOR: + $iterator->getInnerIterator()->getSubPathname(); + + $item_name = ($is_dir) ? basename($iterator->getInnerIterator()->getSubPath()) : + $file_info->getFilename(); if ((!$suffix || substr($relative_path, -strlen($suffix)) === $suffix) && - (!$prefix || substr($file_info->getFilename(), 0, strlen($prefix)) === $prefix) && + (!$prefix || substr($item_name, 0, strlen($prefix)) === $prefix) && (!$directory || preg_match($directory_pattern, DIRECTORY_SEPARATOR . $relative_path))) { $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . $relative_path); diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php index cae11a5bfa..b8ce8909ee 100644 --- a/tests/extension/finder_test.php +++ b/tests/extension/finder_test.php @@ -55,6 +55,18 @@ class phpbb_extension_finder_test extends phpbb_test_case ); } + public function test_prefix_get_directories() + { + $dirs = $this->finder + ->directory('/type') + ->get_directories(); + + sort($dirs); + $this->assertEquals(array( + 'ext/foo/type/', + ), $dirs); + } + public function test_prefix_get_classes() { $classes = $this->finder @@ -144,6 +156,7 @@ class phpbb_extension_finder_test extends phpbb_test_case 'suffix' => '_class.php', 'prefix' => false, 'directory' => false, + 'is_dir' => false, ); $this->assertEquals($expected_files, $files); @@ -162,6 +175,7 @@ class phpbb_extension_finder_test extends phpbb_test_case 'suffix' => false, 'prefix' => false, 'directory' => false, + 'is_dir' => false, ); $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', new phpbb_mock_cache(array( From fd4259919188df2d7e4667108bf86d9bd22a8e2b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 22:32:20 -0400 Subject: [PATCH 29/67] [feature/extension-manager] Correct formatting of documentation PHPBB3-10323 --- phpBB/includes/extension/finder.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index bcdcc61d76..1c1f150673 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -29,14 +29,15 @@ class phpbb_extension_finder protected $cache_name; /** - * @var array An associative array, containing all search parameters set in - * methods + * An associative array, containing all search parameters set in methods. + * @var array */ protected $query; /** - * @var array A map from md5 hashes of serialized queries to their - * previously retrieved results. + * A map from md5 hashes of serialized queries to their previously retrieved + * results. + * @var array */ protected $cached_queries; From 7d16007d6a1c042389039ab9ab59c073ecd7c933 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 29 Aug 2011 22:51:15 -0400 Subject: [PATCH 30/67] [feature/extension-manager] Prepend the phpbb_root_path if necessary. PHPBB3-10323 --- phpBB/includes/extension/finder.php | 26 +++++++++++++++++++++++--- tests/extension/finder_test.php | 9 ++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index 1c1f150673..ee08b0a82a 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -205,7 +205,7 @@ class phpbb_extension_finder $this->query['suffix'] .= $this->phpEx; $this->query['default_suffix'] .= $this->phpEx; - $files = $this->get_files($cache); + $files = $this->find($cache, false); $classes = array(); foreach ($files as $file) @@ -225,7 +225,7 @@ class phpbb_extension_finder */ public function get_directories($cache = true) { - return $this->find($cache, true); + return $this->find_with_root_path($cache, true); } /** @@ -236,7 +236,27 @@ class phpbb_extension_finder */ public function get_files($cache = true) { - return $this->find($cache, false); + return $this->find_with_root_path($cache, false); + } + + /** + * A wrapper around the general find which prepends a root path to results + * + * @param bool $cache Whether the result should be cached + * @param bool $is_dir Whether the found items should be directories + * @return array An array of paths to found items + */ + protected function find_with_root_path($cache = true, $is_dir = false) + { + $items = $this->find($cache, $is_dir); + + $result = array(); + foreach ($items as $item) + { + $result[] = $this->phpbb_root_path . $item; + } + + return $result; } /** diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php index b8ce8909ee..8d7d38d4d8 100644 --- a/tests/extension/finder_test.php +++ b/tests/extension/finder_test.php @@ -63,7 +63,7 @@ class phpbb_extension_finder_test extends phpbb_test_case sort($dirs); $this->assertEquals(array( - 'ext/foo/type/', + dirname(__FILE__) . '/ext/foo/type/', ), $dirs); } @@ -137,7 +137,7 @@ class phpbb_extension_finder_test extends phpbb_test_case public function test_get_classes_create_cache() { $cache = new phpbb_mock_cache; - $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', $cache, '.php', '_custom_cache_name'); + $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/', $cache, '.php', '_custom_cache_name'); $files = $finder->suffix('_class.php')->get_files(); sort($files); @@ -159,7 +159,6 @@ class phpbb_extension_finder_test extends phpbb_test_case 'is_dir' => false, ); - $this->assertEquals($expected_files, $files); $cache->checkAssociativeVar($this, '_custom_cache_name', array( md5(serialize($query)) => $expected_files, )); @@ -178,7 +177,7 @@ class phpbb_extension_finder_test extends phpbb_test_case 'is_dir' => false, ); - $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/includes/', new phpbb_mock_cache(array( + $finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/', new phpbb_mock_cache(array( '_ext_finder' => array( md5(serialize($query)) => array('file_name'), ), @@ -191,7 +190,7 @@ class phpbb_extension_finder_test extends phpbb_test_case sort($classes); $this->assertEquals( - array('file_name'), + array(dirname(__FILE__) . '/file_name'), $classes ); } From 6c6a7d7992460e301615bcc1e13bee92ecc65724 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 30 Aug 2011 00:06:15 -0400 Subject: [PATCH 31/67] [feature/extension-manager] Extract extension provider functionality from cron PHPBB3-10323 --- phpBB/includes/cron/provider.php | 50 ++------------------- phpBB/includes/extension/provider.php | 65 +++++++++++++++++++++++++++ tests/cron/provider_test.php | 4 +- 3 files changed, 70 insertions(+), 49 deletions(-) create mode 100644 phpBB/includes/extension/provider.php diff --git a/phpBB/includes/cron/provider.php b/phpBB/includes/cron/provider.php index ee0f93a308..42552e1d8e 100644 --- a/phpBB/includes/cron/provider.php +++ b/phpBB/includes/cron/provider.php @@ -22,52 +22,20 @@ if (!defined('IN_PHPBB')) * * @package phpBB3 */ -class phpbb_cron_provider implements IteratorAggregate +class phpbb_cron_provider extends phpbb_extension_provider { - /** - * Array holding all found task class names. - * - * @var array - */ - protected $task_names = array(); - - /** - * An extension manager to search for cron tasks in extensions. - * @var phpbb_extension_manager - */ - protected $extension_manager; - - /** - * Constructor. Loads all available tasks. - * - * Tasks will be looked up in the core task directory located in - * includes/cron/task/core/ and in extensions. Task classes will be - * autoloaded and must be named according to autoloading naming conventions. - * - * Tasks in extensions must be located in a directory called cron or a subdir - * of a directory called cron. The class and filename must end in a _task - * suffix. - * - * @param phpbb_extension_manager $extension_manager phpBB extension manager - */ - public function __construct(phpbb_extension_manager $extension_manager) - { - $this->extension_manager = $extension_manager; - - $this->task_names = $this->find_cron_task_names(); - } - /** * Finds cron task names using the extension manager. * * All PHP files in includes/cron/task/core/ are considered tasks. Tasks * in extensions have to be located in a directory called cron or a subdir * of a directory called cron. The class and filename must end in a _task - * suffix. + * suffix. Additionally all PHP files in includes/cron/task/core/ are + * tasks. * * @return array List of task names */ - public function find_cron_task_names() + public function find() { $finder = $this->extension_manager->get_finder(); @@ -79,14 +47,4 @@ class phpbb_cron_provider implements IteratorAggregate ->default_directory('') ->get_classes(); } - - /** - * Retrieve an iterator over all task names - * - * @return ArrayIterator An iterator for the array of task names - */ - public function getIterator() - { - return new ArrayIterator($this->task_names); - } } diff --git a/phpBB/includes/extension/provider.php b/phpBB/includes/extension/provider.php new file mode 100644 index 0000000000..9b5ec56d30 --- /dev/null +++ b/phpBB/includes/extension/provider.php @@ -0,0 +1,65 @@ +extension_manager = $extension_manager; + + $this->items = $this->find(); + } + + /** + * Finds template paths using the extension manager. + * + * @return array List of task names + */ + abstract function find(); + + /** + * Retrieve an iterator over all items + * + * @return ArrayIterator An iterator for the array of template paths + */ + public function getIterator() + { + return new ArrayIterator($this->items); + } +} diff --git a/tests/cron/provider_test.php b/tests/cron/provider_test.php index 781425e6ab..3a0545ffc6 100644 --- a/tests/cron/provider_test.php +++ b/tests/cron/provider_test.php @@ -27,10 +27,8 @@ class phpbb_cron_provider_test extends PHPUnit_Framework_TestCase public function test_manager_finds_shipped_tasks() { - $task_iterator = $this->provider->find_cron_task_names(); - $tasks = array(); - foreach ($task_iterator as $task) + foreach ($this->provider as $task) { $tasks[] = $task; } From 6ea6d50ccb9607429486a01d3144c7d32322e1b5 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 30 Aug 2011 01:15:43 -0400 Subject: [PATCH 32/67] [feature/extension-manager] Don't cache the phpbb_root_path in the ext manager Otherwise the paths are incorrect from e.g. adm/ PHPBB3-10323 --- phpBB/includes/extension/finder.php | 2 ++ phpBB/includes/extension/manager.php | 14 ++++++++++---- tests/cron/provider_test.php | 2 +- tests/extension/finder_test.php | 4 ++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php index ee08b0a82a..9a0727a50c 100644 --- a/phpBB/includes/extension/finder.php +++ b/phpBB/includes/extension/finder.php @@ -161,6 +161,8 @@ class phpbb_extension_finder */ public function directory($directory) { + $directory = preg_replace('#(?:^|/)\./#', '/', $directory); + if (strlen($directory) > 1 && $directory[strlen($directory) - 1] === '/') { $directory = substr($directory, 0, -1); diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index a6c8ebb3d0..ef714638c3 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -93,7 +93,7 @@ class phpbb_extension_manager */ public function get_extension_path($name) { - return $this->phpbb_root_path . 'ext/' . basename($name) . '/'; + return 'ext/' . basename($name) . '/'; } /** @@ -315,7 +315,13 @@ class phpbb_extension_manager */ public function all_configured() { - return $this->extensions; + $configured = array(); + foreach ($this->extensions as $name => $data) + { + $data['ext_path'] = $this->phpbb_root_path . $data['ext_path']; + $configured[$name] = $data; + } + return $configured; } /** @@ -331,7 +337,7 @@ class phpbb_extension_manager { if ($data['ext_active']) { - $enabled[$name] = $data['ext_path']; + $enabled[$name] = $this->phpbb_root_path . $data['ext_path']; } } return $enabled; @@ -350,7 +356,7 @@ class phpbb_extension_manager { if (!$data['ext_active']) { - $disabled[$name] = $data['ext_path']; + $disabled[$name] = $this->phpbb_root_path . $data['ext_path']; } } return $disabled; diff --git a/tests/cron/provider_test.php b/tests/cron/provider_test.php index 3a0545ffc6..662bb34c13 100644 --- a/tests/cron/provider_test.php +++ b/tests/cron/provider_test.php @@ -19,7 +19,7 @@ class phpbb_cron_provider_test extends PHPUnit_Framework_TestCase 'testext' => array( 'ext_name' => 'testext', 'ext_active' => true, - 'ext_path' => dirname(__FILE__) . '/ext/testext/' + 'ext_path' => 'ext/testext/' ), )); $this->provider = new phpbb_cron_provider($this->extension_manager); diff --git a/tests/extension/finder_test.php b/tests/extension/finder_test.php index 8d7d38d4d8..9031e1cb47 100644 --- a/tests/extension/finder_test.php +++ b/tests/extension/finder_test.php @@ -23,12 +23,12 @@ class phpbb_extension_finder_test extends phpbb_test_case 'foo' => array( 'ext_name' => 'foo', 'ext_active' => '1', - 'ext_path' => dirname(__FILE__) . '/ext/foo/', + 'ext_path' => 'ext/foo/', ), 'bar' => array( 'ext_name' => 'bar', 'ext_active' => '1', - 'ext_path' => dirname(__FILE__) . '/ext/bar/', + 'ext_path' => 'ext/bar/', ), )); From ea46feb11542a9cf54ce083ee0ad03f4c5e02a1e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 30 Aug 2011 01:32:11 -0400 Subject: [PATCH 33/67] [feature/extension-manager] Add support for templates in extensions. This commit adds a template path provider to separate the process of locating (cached) paths in extensions from the template engine. The locator is supplied with a list of paths from the path provider. Admin templates can now be created in ext//adm/style/ and regular templates go into ext//styles/