我正在尝试将我的PHP __autoload函数定义为尽可能灵活且灵活。
以下是我的应用程序结构的细分:
/dev (root)
/my_app
/php
/classes
- Class1.php
- Class2.php
- Class3.php
/scripts
myscript.php (the file I have to include the classes in)
这是相当直接的。我的问题是:如何编写我的__autoload函数,以便我可以包含我想要的任何类,无论调用文件在目录结构中的嵌套程度如何。我知道它与__FILE__
,realpath
和dirname
函数有关,但我不确定将它们组合起来以实现我之后的灵活性的适当方式
这是我做的快速测试:
<?php
echo realpath(dirname(__FILE__)) . "/php/classes/Class1.php";
?>
结果:
/home/mydirectory/dev.mysite.com/my_app/php/scripts/php/classes/Class1.php
如您所见,结果与类所在的位置不匹配。但是,如果我将myscript.php文件移动到/ my_app文件夹中,它将正确打印。
关于使这更灵活的建议?
答案 0 :(得分:3)
我建议调查spl_autoload
。只需将适当的目录添加到include_path
这样的事情可能有助于你开始:
ini_set($your_class_dir_here .PATH_SEPERATOR. ini_get('include_path'));
您必须使用spl_autoload_register
或小写所有文件名提供自己的自动加载器。
这是我自己的一个自动加载器,它使用php命名空间来克服一些目录问题。
<?php
namespace Red
{
// since we don't have the Object yet as we load this file, this is the only place where this needs to be done.
require_once 'Object.php';
/**
* Loader implements a rudimentary autoloader stack.
*/
class Loader extends Object
{
/**
* @var Loader
*/
static protected $instance = null;
/**
* @var string
*/
protected $basePath;
/**
* @return Loader
*/
static public function instance()
{
if (self::$instance == null)
{
self::$instance = new self();
}
return self::$instance;
}
/**
* Initialize the autoloader. Future expansions to the
* autoloader stack should be registered in here.
*/
static public function Init()
{
spl_autoload_register(array(self::instance(), 'autoLoadInNamespace'));
}
/**
* PHP calls this method when a class is used that has not been
* defined yet.
*
* I'm returning a boolean for success which isn't required (php ignores it)
* but makes life easier when the stack grows.
*
* @param string $fullyQualifiedClassName
* @return boolean
*/
public function autoLoadInNamespace($fullyQualifiedClassName)
{
$pathParts = preg_split('/\\\\/', $fullyQualifiedClassName, -1, PREG_SPLIT_NO_EMPTY);
array_unshift($pathParts, $this->basePath);
$pathToFile = implode(DIRECTORY_SEPARATOR, $pathParts) . '.php';
if (file_exists($pathToFile))
{
require_once $pathToFile;
return true;
}
return false;
}
/**
* Constructor is protected because we don't want multiple instances
* But we do want an instance to talk to later on.
*/
protected function __construct()
{
$this->basePath = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..');
}
}
}
#EOF;
它是Loader
命名空间中名为\Red
的类的一部分,并从一个简单的引导文件初始化:
<?php
// This is where the magic is prepared.
require_once implode(DIRECTORY_SEPARATOR, array(dirname(__FILE__), 'Red', 'Loader.php'));
// Initialize the autoloader, no more require_once for every class
// and everything is OOP from here on in.
Red\Loader::Init();
#EOF
答案 1 :(得分:1)
我不会选择自动文件位置检测。除非仔细考虑它可能是一个安全漏洞,否则您需要设计一个支持子目录的命名原理图,它会强制您为每个类使用一个文件(这可能是好事也可能是坏事,但我发现它不灵活)。我会使用全局或静态数组,你保持映射className =&gt; pathToClass。
答案 2 :(得分:1)
$_SERVER['DOCUMENT_ROOT']
应包含Web服务器根目录的完整路径。从那里你应该能够继续通过文件夹结构到类目录的路径。如果您将应用程序名称存储在会话中,则可以在任何地方使用相同的代码。
//set in config file
if(!isset($_SESSION['APP_DIR'])) $_SESSION['APP_DIR'] = "my_app";
//autoload
//builds a string with document root/app_name/classes
//takes the app name and replaces anything not alphanumeric or _ with an _ and
//makes it lowercase in case of case sensitive. as long as you follow this naming
//scheme for app directories it should be fine for multiple apps.
$classPath = $_SERVER['DOCUMENT_ROOT'] . '/' .
strtolower(preg_replace('/\W+/', '_', $_SESSION['APP_DIR'])) .
'/classes/';
答案 3 :(得分:0)
基本上有两种方式。您可以指定类目录(/home/mydirectory/dev.mysite.com/my_app/php/classes/
)的完整绝对路径,我不建议这样做,因为它涉及更改主机时更改绝对路径。或者您可以使用相对路径,这样更容易和便携:
require_once '../classes/'.$classname;
无需在此处获取realpath
,PHP可以使用相对路径。 ;)
PS:我认为realpath(dirname(__FILE__))
是重复的。 __FILE__
已经是“真正的路径”,因此您无需致电realpath
。