1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-03-13 20:28:44 +01:00

[feature/auto-loading] Added a phpBB autoloader with caching support.

phpBB class name lookups follow these rules:
 - All classes are prefixed with phpbb_
 - All classes reside in includes/ or a subdirectory thereof
 - Directories must not contain underscores
 - The class name is separated into parts by underscores, the parts
   are checked from first to last, until one is found which is not
   a directory, all remaining parts make up the file name. If no parts
   are left, the last directory name is used.

Examples:
  directory structure:
    includes/
      class_name.php
      dir/
        class_name.php
        dir.php
        subdir/
          class_name.php

  lookups:
    phpbb_class_name            -> includes/class_name.php
    phpbb_dir_class_name        -> includes/dir/class_name.php
    phpbb_dir                   -> includes/dir/dir.php
    phpbb_dir_subdir_class_name -> includes/dir/subdir/class_name.php

Optionally the class can be supplied with a cache instance, either in the
constructor or via set_cache() at a later time. This allows for the lookups to
be cached, so the directories do not have to be traveresed on every request.
This makes it necessary for the cache and its dependency to continue to be
loaded the old way - without autoloading.

The code will not be changed to use autoloading, but it will rather only be
used for new classes where applicable.

PHPBB3-9682
This commit is contained in:
Nils Adermann 2010-06-25 14:24:04 +02:00
parent 55a22363fa
commit 22c3041e12
8 changed files with 317 additions and 0 deletions

View File

@ -0,0 +1,162 @@
<?php
/**
*
* @package phpBB3
* @version $Id$
* @copyright (c) 2005 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* The class loader resolves class names to file system paths and loads them if
* necessary.
*
* Classes have to be of the form phpbb_(dir_)*(classpart_)*, so directory names
* must never contain underscores. Example: phpbb_dir_subdir_class_name is a
* valid class name, while phpbb_dir_sub_dir_class_name is not.
*
* If every part of the class name is a directory, the last directory name is
* also used as the filename, e.g. phpbb_dir would resolve to dir/dir.php.
*
* @package phpBB3
*/
class phpbb_class_loader
{
private $phpbb_root_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.
*
* @param string $phpbb_root_path phpBB's root directory containing includes/
* @param string $php_ext The file extension for PHP files
*/
public function __construct($phpbb_root_path, $php_ext = '.php', $cache = null)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->set_cache($cache);
}
/**
* Provide the class loader with a cache to store paths. If set to null, the
* the class loader will resolve paths by checking for the existance of every
* directory in the class name every time.
*
* @param acm $cache An implementation of the phpBB cache interface.
*/
public function set_cache($cache = null)
{
if ($cache)
{
$this->cached_paths = $cache->get('class_loader');
if ($this->cached_paths === false)
{
$this->cached_paths = array();
}
}
$this->cache = $cache;
}
/**
* Registers the class loader as an autoloader using SPL.
*/
public function register()
{
spl_autoload_register(array($this, 'load_class'));
}
/**
* Removes the class loader from the SPL autoloader stack.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'load_class'));
}
/**
* Resolves a phpBB class name to a relative path which can be included.
*
* @param string $class The class name to resolve, must have a phpbb_
* prefix
* @return string|bool A relative path to the file containing the
* class or false if looking it up failed.
*/
public function resolve_path($class)
{
$path_prefix = $this->phpbb_root_path . 'includes/';
if (isset($this->cached_paths[$class]))
{
return $path_prefix . $this->cached_paths[$class] . $this->php_ext;
}
if (!preg_match('/phpbb_[a-zA-Z0-9_]+/', $class))
{
return false;
}
$parts = explode('_', substr($class, 6));
$dirs = '';
for ($i = 0; is_dir($path_prefix . $dirs . $parts[$i]) && $i < sizeof($parts); $i++)
{
$dirs .= $parts[$i] . '/';
}
// no file name left => use last dir name as file name
if ($i == sizeof($parts))
{
$parts[] = $parts[$i - 1];
}
$relative_path = $dirs . implode(array_slice($parts, $i, sizeof($parts) - $i), '_');
if (!file_exists($path_prefix . $relative_path . $this->php_ext))
{
return false;
}
if ($this->cache)
{
$this->cached_paths[$class] = $relative_path;
$this->cache->put('class_loader', $this->cached_paths);
}
return $path_prefix . $relative_path . $this->php_ext;
}
/**
* Resolves a class name to a path and then includes it.
*
* @param string $class The class name which is being loaded.
*/
public function load_class($class)
{
if (substr($class, 0, 6) === 'phpbb_')
{
$path = $this->resolve_path($class);
if ($path)
{
require $path;
}
}
}
}

View File

@ -15,6 +15,7 @@ if (!defined('PHPUnit_MAIN_METHOD'))
require_once 'test_framework/framework.php';
require_once 'PHPUnit/TextUI/TestRunner.php';
require_once 'class_loader/all_tests.php';
require_once 'utf/all_tests.php';
require_once 'request/all_tests.php';
require_once 'security/all_tests.php';
@ -38,6 +39,7 @@ class phpbb_all_tests
{
$suite = new PHPUnit_Framework_TestSuite('phpBB');
$suite->addTest(phpbb_class_loader_all_tests::suite());
$suite->addTest(phpbb_utf_all_tests::suite());
$suite->addTest(phpbb_request_all_tests::suite());
$suite->addTest(phpbb_security_all_tests::suite());

View File

@ -0,0 +1,41 @@
<?php
/**
*
* @package testing
* @copyright (c) 2008 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
if (!defined('PHPUnit_MAIN_METHOD'))
{
define('PHPUnit_MAIN_METHOD', 'phpbb_class_loader_all_tests::main');
}
require_once 'test_framework/framework.php';
require_once 'PHPUnit/TextUI/TestRunner.php';
require_once 'class_loader/class_loader_test.php';
class phpbb_class_loader_all_tests
{
public static function main()
{
PHPUnit_TextUI_TestRunner::run(self::suite());
}
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('phpBB Class Loader');
$suite->addTestSuite('phpbb_class_loader_test');
return $suite;
}
}
if (PHPUnit_MAIN_METHOD == 'phpbb_class_loader_all_tests::main')
{
phpbb_class_loader_all_tests::main();
}

View File

@ -0,0 +1,29 @@
<?php
/**
*
* @package testing
* @version $Id$
* @copyright (c) 2008 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
class phpbb_cache_mock
{
private $variables = array();
function get($var_name)
{
if (isset($this->variables[$var_name]))
{
return $this->variables[$var_name];
}
return false;
}
function put($var_name, $value)
{
$this->variables[$var_name] = $value;
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
*
* @package testing
* @version $Id$
* @copyright (c) 2008 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
require_once 'test_framework/framework.php';
require_once 'class_loader/cache_mock.php';
require_once '../phpBB/includes/class_loader.php';
class phpbb_class_loader_test extends PHPUnit_Framework_TestCase
{
public function test_resolve_path()
{
$prefix = 'class_loader/';
$class_loader = new phpbb_class_loader($prefix);
$prefix .= 'includes/';
$this->assertEquals(
$prefix . 'class_name.php',
$class_loader->resolve_path('phpbb_class_name'),
'Top level class'
);
$this->assertEquals(
$prefix . 'dir/class_name.php',
$class_loader->resolve_path('phpbb_dir_class_name'),
'Class in a directory'
);
$this->assertEquals(
$prefix . 'dir/subdir/class_name.php',
$class_loader->resolve_path('phpbb_dir_subdir_class_name'),
'Class in a sub-directory'
);
}
public function test_resolve_cached()
{
$cache = new phpbb_cache_mock;
$cache->put('class_loader', array('phpbb_a_cached_name' => 'a/cached_name'));
$prefix = 'class_loader/';
$class_loader = new phpbb_class_loader($prefix, '.php', $cache);
$prefix .= 'includes/';
$this->assertEquals(
$prefix . 'dir/class_name.php',
$class_loader->resolve_path('phpbb_dir_class_name'),
'Class in a directory'
);
$this->assertEquals(
$prefix . 'a/cached_name.php',
$class_loader->resolve_path('phpbb_a_cached_name'),
'Class in a directory'
);
}
}

View File

@ -0,0 +1,6 @@
<?php
class phpbb_class_name
{
}

View File

@ -0,0 +1,6 @@
<?php
class phpbb_dir_class_name
{
}

View File

@ -0,0 +1,6 @@
<?php
class phpbb_dir_subdir_class_name
{
}