我有很多我在网站上包含的功能和课程。 在stackoverflow的帮助下,我收到了一个脚本,该脚本自动包含文件夹及其子文件夹中的所有文件:PHP: Automatic Include
在测试脚本时,它始终有效,我从来没有遇到任何问题。 但是最近从Windows服务器切换到Linux服务器时,它会产生类扩展的问题。
PHP致命错误:在第3行的路径/函数/类/ Acumulus / AcumulusExportWorkshop.php中找不到类'AcumulusExportBase',参考:pagesite /?page_id = 346
AcumulusExportWorkshop从AcumulusExportBase扩展而来。 这一切都完全适用于Windows,但拒绝在Linux上工作。
我可以修复这个创建一个include_once'AcumulusExportBase.php';但如果有一个更好的解决方案,那么这一切似乎都是不必要的,并且正在努力工作。
我使用的代码如下:
load_folder(dirname(__FILE__));
function load_folder($dir, $ext = '.php') {
if (substr($dir, -1) != '/') { $dir = "$dir/"; }
if($dh = opendir($dir)) {
$files = array();
$inner_files = array();
while($file = readdir($dh)) {
if($file != "." and $file != ".." and $file[0] != '.') {
if(is_dir($dir . $file)) {
$inner_files = load_folder($dir . $file);
if(is_array($inner_files)) $files = array_merge($files, $inner_files);
} else {
array_push($files, $dir . $file);
}
}
}
closedir($dh);
foreach ($files as $file) {
if (is_file($file) and file_exists($file)) {
$lenght = strlen($ext);
if (substr($file, -$lenght) == $ext && $file != 'loader.php') { require_once($file); }
}
}
}
}
有谁能告诉我windows是如何扩展类和linux没有问题的?还有一个问题的解决方案,而无需手动包括基类?
答案 0 :(得分:6)
您是否确认在Linux下的 AcumulusExportWorkshop 之前包含 AcumulusExportBase ? PHP对导入顺序很敏感。
答案 1 :(得分:5)
其他两个答案都是正确的(我已经对它们进行了投票)。您的问题将是文件加载的顺序(请参阅Mark的响应),并且递归也是错误的(请参阅KIKO)。
然而,有一种更好的方法可以做你想做的事:使用自动加载器。 http://php.net/manual/en/language.oop5.autoload.php
第一次令人困惑,但是一旦你掌握了它,这是一种加载文件的可爱方式。
基本上你说“如果我需要X级并且没有加载,那么加载文件Y.php”。
如果你是超级懒惰并且不想指定每个类,那么你可以说“如果我需要X类并且它没有加载,请运行目录结构,寻找名为X.php
的文件并加载,我的班级将在那里。“你可以混合上面的内容来做到这一点。
这样,你可以首先加载AcumulusExportWorkshop,然后它会在之后查找AcumulusExportBase并快乐地运行。
更有利的是,您只需加载您需要的东西。如果你从不需要这个类,它永远不会被加载。
答案 2 :(得分:2)
我想回答你的问题,但遗憾的是我没有安装Windows PHP服务器。但是,我可以查看并测试您的代码。我注意到的第一件事是错误的递归。要获取'inner_files',使用递归,这很好,但这需要你的函数返回一个值,即文件数组。它不是。此外,尽管您正在使用'require_once',但每次递归都会调用此函数,这意味着您尝试多次包含“深层”文件。简而言之:现在是时候简化代码了。
load_folder(dirname(__FILE__));
function load_folder($dir,$ext = '.php')
{
if (substr($dir,-1) != '/') $dir = $dir.'/';
if ($handle = opendir($dir))
{
while($file = readdir($handle))
{
if (($file != '.') && ($file != '..') && ($file[0] != '.'))
{
if (is_dir($dir.$file)) load_folder($dir.$file,$ext);
else
{
if ((substr($file,-strlen($ext)) == $ext) &&
($file != 'loader.php') &&
file_exists($dir.$file)) require_once($dir.$file);
}
}
}
closedir($handle);
}
}
这在linux下运行,并执行相同的任务。我更正了内部load_folder()中缺少$ ext的事实。
我的建议是永远不要盲目复制您在互联网上找到的代码。务必检查,然后再次检查。确保你了解它是如何工作的。如果你不这样做,你的项目就会被错误所困扰,任何人都无法维护。
答案 3 :(得分:2)
正如Robbie所说,其他两个答案都是正确的,自动加载器是理想的解决方案。
自动加载器看起来(起初)可能稍微复杂一点,但它带来了真正意义重大的好处并且值得使用。
以下是其中一些:
您无需手动添加或要求文件。
您无需担心以正确的顺序加载文件 - 解释器将自动加载任何依赖项。 (在您的情况下,Windows和Linux操作系统的差异暴露了现有代码中的这个弱点)
您可以避免加载不需要的文件。
解释器不需要解析不必要的类和代码。
以下是您应该了解的有关自动加载器的一些事项:
您可以根据需要拥有尽可能多的自动加载器 - 它们存储在堆栈中并按顺序执行。如果第一个没有加载类,则使用下一个类,依此类推,直到加载类或没有更多自动加载器可以尝试。
自动加载器是可调用的 - 可以是类的方法,也可以是函数。
您实际上如何实现自动加载器取决于您 - 因此,如果您的项目具有与类类型或层次结构相关的特定目录结构,则可以指示它查看特定目录,从而提高效率。
我们大多数人都希望将我们的课程保存在单独的文件中。这样可以更容易地找到我们感兴趣的类,并使文件更小,这使它们更容易理解。
当涉及到我们使用的文件的名称时,PHP没有强制执行任何类型的命名约定,但是大多数开发人员更喜欢将Classes保存在文件名中与类名相关的文件中。
自动加载器功能假定在显示文件名时有一种方法可以加载正确的文件。因此,一个好的做法是使用一种简单的方法从类名生成文件名 - 最简单的方法是使用类名作为文件名。
这是我首选的自动加载器 - 我从Jess Telford的代码改编而来,当我在学习PHPUnit时,我在网上找到了 - (http://jes.st/2011/phpunit-bootstrap-and-autoloading-classes/)
class ClassDirectoryAutoLoader {
static private $classNamesDirectory = array();
public static function crawlDirectory($directory) {
$dir = new DirectoryIterator($directory);
foreach ($dir as $file) {
self::addClassesAndCrawlDirectories($file);
}
}
private static function addClassesAndCrawlDirectories($file){
if (self::isRealDirectory($file)) {
self::crawlDirectory($file->getPathname());
} elseif (self::isAPhpFile($file)) {
self::saveClassFilename($file);
}
}
private static function isRealDirectory($file){
// ignore links, self and parent
return $file->isDir() && !$file->isLink() && !$file->isDot();
}
private static function isAPhpFile($file){
//ends in .php
return substr($file->getFilename(), -4) === '.php';
}
private static function saveClassFilename($file){
//assumes that the filename is the same as the classname
$className = substr($file->getFilename(), 0, -4);
self::registerClass($className, $file->getPathname());
}
public static function registerClass($className, $fileName) {
self::$classNamesDirectory[$className] = $fileName;
}
public static function loadClass($className) {
if (isset(self::$classNamesDirectory[$className])) {
require_once(self::$classNamesDirectory[$className]);
}
}
}
$classDir = dirname(__FILE__) . '/../classes'; // replace with the root directory for your class files
ClassDirectoryAutoLoader::crawlDirectory($classDir);
spl_autoload_register(array('ClassDirectoryAutoLoader', 'loadClass'));
此代码的作用是
递归目录(来自classDir),查找.php文件。
构建一个将类名映射到完整文件名的关联数组。
注册自动加载器(loadClass方法)。
当解释器试图实例化一个未定义的类时,它将运行这个自动加载器,它将:
检查文件是否存储在关联数组中。
找到找到的文件。
我喜欢这款自动加载器,因为:
这很简单。
它的一般性 - 您几乎可以在任何遵循一些简单约定的项目中使用它(见下文)。
它只抓取目录树一次,而不是每次实例化一个新类。
它只需要所需的文件。
loadClass方法非常高效,只需执行查找和执行即可。
这段代码做了一些假设:
所有课程都存储在特定目录中。
该目录中的所有文件都包含类。
文件名与班级名称完全匹配。
要求文件没有副作用(即文件只包含类定义,没有程序代码)。
打破这些假设会打破这个自动加载器。
这些是您使用此自动加载器时需要遵循的约定:
将所有课程保存在一个目录下。
只有类目录下的类定义文件。
为每个公共类制作一个单独的文件。
在每个文件包含的类之后命名。
这些类定义文件中没有带副作用的过程代码。
答案 4 :(得分:2)
好吧,我正在构建一个使用自动加载器的系统,所以这就是我所做的:
function endswith($string,$tidbit){
// Length of string
$strlen = strlen($string);
// Length of ending
$tidlen = strlen($tidbit);
// If the tidbit is of the right length (less than or equal to the length of the string)
if($tidlen <= $strlen){
// Substring requires a place to start the copying
$tidstart = $strlen - $tidlen;
// Get $tidlen characters off the end of $string
$endofstring = substr($string, $tidstart, $tidlen);
// If the $tidbit matches the end of the string
$ret = ($endofstring == $tidbit);
return $ret;
} else {
// Failure
return -1;
}
}
// Working
function ush_load_path($path) {
if (is_dir($path)) {
if (is_file($path . '/' . (explode('/', $path)[count(explode('/', $path)) - 1]) . '.inc')) {
require_once $path . '/' . (explode('/', $path)[count(explode('/', $path)) - 1]) . '.inc';
}
ush_load_path_recursive($path);
// If it is a file
} else if (is_file($path)) {
require_once $path;
// Failure
} else {
return false;
}
}
function ush_load_path_recursive($path) {
// Directory RESOURCE
$path_dir = opendir($path);
// Go through the entries of the specified directory
while (false != ($entry = readdir($path_dir))) {
if ($entry != '.' && $entry != '..') {
// Create Full Path
$path_ext = $path . '/' . $entry;
// Development
if (is_dir($path_ext)) {
ush_load_path_recursive($path_ext);
} else if (is_file($path_ext)) {
if (ush_is_phplib($path_ext)) {
print $path_ext . '<br />';
require_once $path_ext;
} else {
// Do nothing
}
}
}
}
}
// Working
function ush_is_phplib($path) {
return endswith($path, '.inc');
}
答案 5 :(得分:1)
load_folder(dirname(__FILE__));
function load_folder($dir, $ext = '.php') {
if (substr($dir, -1) != '/') { $dir = "$dir/"; }
clearstatcache(); // added to clear path cache
if($dh = opendir($dir)) {
$files = array();
$inner_files = array();
while($file = readdir($dh)) {
if($file != "." and $file != ".." and $file[0] != '.') {
if(is_dir($dir . $file)) {
$inner_files = load_folder($dir . $file);
if(is_array($inner_files)) $files = array_merge($files, $inner_files);
} else {
array_push($files, $dir . $file);
}
}
}
closedir($dh);
clearstatcache($dir); // added to clear path cache
foreach ($files as $file) {
if (is_file($file) and file_exists($file)) {
$lenght = strlen($ext);
if (substr($file, -$lenght) == $ext && $file != 'loader.php') { require_once($file); }
}
}
}
}
似乎必须在符号链接目录上的任何文件处理函数之前调用clearstatcache($ path)。 Php没有正确缓存符号链接。
答案 6 :(得分:1)
你可以在closedir($ dh)之后做一个print_r($ files);在foreach之前,我们可以看到哪些文件实际上正在加载以及按哪种顺序?
答案 7 :(得分:1)
我可能在这里错过了一些观点,但它让我看到了那个脚本......我假设你用一个文件夹调用该函数,你保存所有的php函数文件,不是包含所有内容,即使实际脚本只需要其中一个文件也可以使用。
我在这里遗漏了什么,或者它是如何工作的?如果没有,我会被描述和功能代码误导。
如果这正是你正在做的事情,那么有更好的方法来包含所需的文件,而没有所有这些不必要的内容。
我有一个处理我所有脚本加载的类。我所要做的就是注册加载函数:
spl_autoload_register('Modulehandler::Autoloader');
然后,只要需要新文件,PHP就会使用我的函数来查找文件。
这是函数本身:
static function Autoloader($className) {
$files = array($className);
$lowerClass = strtolower($className);
if (strcmp($className, $lowerClass) != 0) $files[] = $lowerClass;
foreach (self::$modules as $moduleName => $module) {
foreach ($files as $className) {
$file = "{$module}/classes/{$className}.php";
if (file_exists($file)) {
require $file;
break;
}
}
}
}
这个类本身比加载更多,因为我还能够将模块添加到加载器,所以我只搜索包含模块中的文件夹,从而在搜索范围内获得一些性能提升所有模块。除此之外,只有包含必要的文件才有明显的好处。
我希望这符合你的需要,并帮助你。
答案 8 :(得分:1)
您是否检查过AcumulusExportBase是否正确包含在AcumulusExportWorkshop中? 请记住,Linux非常区分大小写,所以文件名应该在适当的情况下。
LIKE a.JPG可以在windows中被称为a.jpg但在LINUX中我们需要保持正确的情况。
答案 9 :(得分:1)
主要区别在于文件系统的类型。当您使用Mac或Windows时,它们都不区分大小写,但Linux实际上将文件名“Capitalized.php”视为“CAPITALIZED.PHP”
这就是为什么大多数流行的框架都有较低的文件名。