【问题标题】:PHP - Prevent autoloader errorPHP - 防止自动加载错误
【发布时间】:2015-05-27 11:17:03
【问题描述】:

我的 php.ini 中有一个 MVC 结构。

我使用自动加载来初始化正确的控制器。

这是我的 index.php 的一个非常简单的版本:

<?php
spl_autoload_extensions('.class.php');
spl_autoload_register();

$controller = strtolower($_GET["controller"]);
$action = strtolower($_GET["action"]);

$obj = new $controller();
$obj->{$action}();

假设用户加载了www.example.com/Page/View。 Apache 会将其重写为www.example.com?controller=Page&amp;action=View。然后当 php 调用$obj = new $controller(); 时,它会尝试在与文件相同的目录中找到page.class.php(显然在我的应用程序中,文件结构不是那么简单,有命名空间等......),然后加载Page 类里面并执行Page-&gt;View();

现在,假设用户打错字并尝试加载www.example.com/Pagr/View。理想情况下,他应该得到 404 标头响应。但是在当前的实现中,当 php 无法自动加载 pagr.class.php 时,它只会抛出一个 Fatal error

如何防止此错误发生?我已经进行了一些研究,但我无法在new 调用之前检查类是否可以自动加载。

【问题讨论】:

  • 使用 try-catch 块怎么样?尝试{ $obj = new $controller(); } catch(ExceptionThrownOnNotClassFound){ //创建 404 响应 }
  • 无法捕获致命错误。
  • 我只是想在这里猜测一下。也许用 class_exists 检查类 $obj 是否存在可以帮助你
  • 只是说:不要盲目使用来自 $_GET 的输入值。用户可能会制作一些令人讨厌的网址,这些网址可能会带来意想不到的危险后果。

标签: php oop autoload spl-autoload-register


【解决方案1】:

在尝试加载类之前,使用class_exists() 检查类是否存在。

但是,我会这样做:

class ClassNotFoundException extends Exception {}

function autoloadClass($class) {
  $file = $class . '.class.php';

  if(!file_exists($file)) {
    throw new ClassNotFoundException($class);
  }

  require($file);
}

spl_autoload_register('autoloadClass');

try {
  $obj = new $controller();
}
catch(ClassNotFoundException $e) {
  // call 404 page
}

【讨论】:

  • class_exists() 检查一个类是否已经加载。它不检查是否可以自动加载。
  • 你不能检查文件是否存在?
  • @halloei 您的实现是最明显的一个,然而,人们报告说显然 php 忽略了 __autoload 函数内抛出的异常,并且无论如何都会抛出 Fatal error。见this
  • 我目前认为最简单的方法可能是尝试检查class_exists 并捕获它抛出的异常。我要试试。
  • 注意:在 5.3.0 之前,在 __autoload 函数中抛出的异常无法在 catch 块中捕获,会导致致命错误。从 5.3.0+ 开始,在 __autoload 函数中抛出的异常可以在 catch 块中捕获,有 1 个规定。如果抛出自定义异常,则自定义异常类必须可用。 __autoload 函数可用于递归地自动加载自定义异常类。
【解决方案2】:

@halloei 是正确的,但需要注释来解释为什么它是正确的。 class_exists 是另一个有趣的 PHP 函数,尝试自动加载类以防万一它不存在。这是非常违反直觉的,但这就是它的工作原理(此行为可以通过第二个参数关闭)。所以,class_exists 不会告诉你类是否存在,而是告诉你它是否存在或是否在注册的自动加载器范围内。

【讨论】:

  • 嗯。当我尝试在无法自动加载的类上运行class_exists 时,它会抛出LogicExceptionThis is apparently a bug in php.
  • @Nikita240 我猜路径检查/异常捕获是你唯一的选择。但我强烈建议您避免这种模式并重写架构,因为任何人都可以在任何课程上调用__construct(),甚至做更糟糕的事情。
  • 您能否详细说明架构问题?
  • @Nikita240 我会实现一个简单的路由器,只是将正则表达式模式映射到特定的控制器操作(例如['user/(\d+)' =&gt; 'User/Display'])并针对这些正则表达式检查请求 uri。据我所知,这不是一个大项目,所以如此简单地实施它不会是一个麻烦(我为任何项目都采用了框架,但这不是我的决定),这将限制什么可以被执行。 “没有匹配”的情况应该只是呈现一个 404 页面。
【解决方案3】:

除非您绝对需要一种方法来检查类是否可以自动加载。否则,如果需要在实例化对象之前检查控制器文件是否存在,可以尝试:

if(file_exists(strtolower($controller).'.class.php') && class_exists ($controller, false))
{
   $obj = new $controller();
}

传递给class_exists() 的第二个参数确保它不会尝试自动加载类。我不知道这是否对您有任何帮助。

【讨论】:

  • 好吧,它必须更复杂一些,因为要正确实现,您必须计算包含路径和命名空间,但是您可以检查文件是否存在。但问题是,如果在文件中找不到该类,它仍然会抛出错误,在这种情况下,您仍然希望返回 404。
  • 您最好的选择是将上面的检查与@halloei 的建议结合起来。我会更新我的答案以反映我的意思。
【解决方案4】:

这是一种方法。

class_exists() 应该会尝试自动加载尚未定义的类。但是,当前在 php 中有一个 bug,如果自动加载器失败,它会使 class_exists() 抛出一个 LogicException。但是我们可以简单地捕获这个异常,直到错误被修复!

<?php
spl_autoload_extensions('.class.php');
spl_autoload_register();

$controller = strtolower($_GET["controller"]);
$action = strtolower($_GET["action"]);

try {
    $class_exists = class_exists($controller);
}
catch(LogicException $e) {
    $class_exists = false;
}
finally {
    if(!$class_exists) {
        http_response_code(404);
        exit();
    }
}

$obj = new $controller();
$obj->{$action}();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-31
    • 2016-01-07
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多