在Magento中处理绝望的类型提示


15

只是想知道是否有人比我想的更好的策略可以与Magento的自定义错误处理程序共存。具体来说,我想知道在类型提示参数不匹配的情况下抛出的“可捕获的致命错误”。这是该Mage班的一个例子:

/**
 * Write exception to log
 *
 * @param Exception $e
 */
public static function logException(Exception $e)
{
    if (!self::getConfig()) {
        return;
    }
    $file = self::getStoreConfig('dev/log/exception_file');
    self::log("\n" . $e->__toString(), Zend_Log::ERR, $file);
}

由于有错误处理程序,任何东西都可以传递给该方法,包括Zend_Date(可以正常工作,但在您的异常日志中看起来超级混乱)或一个Mage_Core_Model_App(实际上会出现致命错误)。

可以在方法顶部重新实现类型检查:$e instanceof Exception但是这种策略无法实现类型提示的目的。

有什么提示建议吗?

Answers:


5

好问题+1

在与我的第一个答案与@mpw讨论之后,对方向做了很好的说明之后,做了一些研究和测试。我第一次对它有所误解。

将添加一些代码来澄清,以便其他人更好地理解该问题。

起飞前的注意事项

在出现这种情况之前,我从未遇到过此类问题。在启用了开发人员模式的情况下在Magento中进行开发,我什至不考虑这一点。因此,每次放屁时,都会出现并相应地纠正。

解释样本的问题

您所说的致命错误将被记录(如果启用),并且代码将照常继续执行,因为不会引发任何错误,mageCoreErrorHandler或者程序会抛出错误exit

第一个Magento的核心错误处理程序,用于不可捕获的错误 app/code/core/Mage/Core/functions.php

/**
 * Custom error handler
 *
 * @param integer $errno
 * @param string $errstr
 * @param string $errfile
 * @param integer $errline
 */
function mageCoreErrorHandler($errno, $errstr, $errfile, $errline){
    /**
     * Some internal logic here for building the error message
     */

    $errorMessage .= ": {$errstr}  in {$errfile} on line {$errline}";
    if (Mage::getIsDeveloperMode()) {
        throw new Exception($errorMessage);
    } else {
        Mage::log($errorMessage, Zend_Log::ERR);
    }
}

如您所见,在开发人员模式下它将告诉您一些有用的信息,并引发错误。关闭时,它将记录(如果启用)并继续。

证据

我的 testfile.php

require 'app/Mage.php';
Mage::app('admin')->setUseSessionInUrl(false);

// Test function which expect Customer_Model_Customer
function test(Customer_Model_Customer $customer)
{
    var_dump('Do not show me because ' . get_class($customer) . ' is not a customer.');
}

// Enabled developer mode
Mage::setIsDeveloperMode(true);

// Put a var in here
$noGood = Mage::app();

// Make some context
var_dump('hello');
try {
    // Call test function with a not accepted var
    test($noGood);

    // Tell if we get here
    var_dump('And we are here!');

} catch (Exception $e) {
    var_dump('You should die, because I am doing something which I should not do');
}

结果

开发人员模式已启用。正确的结果

string(5) "hello"
string(66) "You should die, because I am doing something which I should not do"

禁用开发人员模式,结果不正确

string(5) "hello"
string(61) "Do not show me because Mage_Core_Model_App is not a customer."
string(16) "And we are here!"

因此,它最终将跳过该错误,并继续执行下一行代码。也许结果甚至更奇怪。(正如@mpw指出的)

结论

可能发生有人的方式发展的错误会被忽视,它最终放弃意想不到的效果。

当然,以专业的方式发展。错误将发现并引起注意。在Magento中防止这种情况的方法始终是在开发人员/测试环境中启用开发人员模式。

恕我直言,它永远都不应达到讨论的目的,在这种情况下,第二次检查变量(至少是我要描述的方式)是可行的。在生产环境上发布代码之前,应先对其进行测试。它应该被需要。

第二个想法

也许Magento应该在发生致命错误后停止。或生成报告并将其显示给访问者。这样,下一行代码将永远不会执行,并且一切都会被注意到。


>以专业方式开发时较粗糙。错误将被发现并引起注意。在Magento中防止这种情况的方法始终是在开发人员/测试环境中启用开发人员模式。¶我同意这一点。我的目标是让Magento在生产模式下尊重语言规则。看起来可能需要自定义模块。感谢您的见解!
mpw

在这两种情况下,Magento都应该抛出异常。将向用户显示Magento错误日志页面,并且在var / exception中将具有匹配的日志文件,与常规异常相同。这里最大的收获是代码不会在没有通知的情况下执行。您可以将函数文件复制到app / code / local并始终引发异常
Jeroen 2015年

1
我决定将其标记为答案。尽管我仍然认为像这样的消音错误是危险的,但是似乎不可能有一种方法来确保Magento尊重打字提示而不引起其他问题。对于未来的读者来说,保持开发模式开启的提醒是一个不错的选择,这是最重要的要点
mpw

2

好问题。我认为这是E_RECOVERABLE_ERRORPHP中的普遍问题。

问题中的内容是异常处理程序,而不是错误处理程序。错误处理程序引起您在此处讨论的实际问题,并带有可捕获的致命错误E_RECOVERABLE_ERROR

PHP 7和HHVM已经解决了这一问题。

Magento的情况更糟,因为自PHP 5.2错误类以来,错误处理程序不再对此进行处理。

一种更有用的错误处理方法是处理此类错误,并将这些错误转换为ErrorException。示例(不是我的,从这里开始):

set_error_handler(function($errno, $errstr, $errfile, $errline) {
    if ($errno === E_RECOVERABLE_ERROR) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
    return false;
});

所以,在Magento的光,默认的错误处理程序是全局函数mageCoreErrorHandlerapp/code/core/Mage/Core/functions.php。它得到通过注册的Mage::app()init()该法Mage_Core_Model_Appapp/code/core/Mage/Core/Model/App.php)(通过保护_initEnvironment()法)。

这样,controller_front_init_before其上面注册了您自己的PHP错误处理程序的观察者就足够了(PHP中的错误处理程序是可堆栈的):

$previous = set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$previous) {
    if ($errno === E_RECOVERABLE_ERROR) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
    if ($previous) {
        return call_user_func($previous, $errno, $errstr, $errfile, $errline);
    }
    return false;
});

可捕获的致命错误然后变成异常,您可以在自己的扩展代码中对其进行处理,或者将它们捕获,并在异常日志中看到它们(而不是让商店对当前类型为错误的错误类型执行加加,死程序不要说谎)。在PHP 7中,要查找的异常不是ErrorException,而是 TypeException(这是一个BaseException)对于现在开捕致命错误

所有其他错误都传递给Magento的错误处理程序。

注意:我还没有尝试过,这是一篇文章,但我知道您要问的问题,并且针对代码1.5.1.0进行了错误处理分析,并通过代码分析针对1.9.1.0进行了验证。错误处理程序堆栈应该可以工作。我附加了一些扩展的示例代码,以演示大多数部分的工作原理。

我还没有将其打包为magento扩展,但应该与modman一起使用。然后将其放在github上。

附录:错误处理程序演示

以下代码示例(在线演示)演示了错误处理程序的堆栈以及在可捕获的致命错误引发的异常

<?php
/**
 * error handler demonstration
 *
 * stackable error handle with previous call and catchable error exceptions
 *
 * @author hakre <http://hakre.wordpress.com>
 * @link /magento//a/64972/4115
 */

set_error_handler(function() {
    $args = func_get_args();
    var_dump("me is the previous error handler", $args);
});

$previous = set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$previous) {
    if ($errno === E_RECOVERABLE_ERROR) {
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
    if ($previous) {
        return call_user_func($previous, $errno, $errstr, $errfile, $errline);
    }
    return false;
});

$test = function(callable $test) {};

$a = $undefined; // provoke little warning

$test(new stdClass); // provoke catchable fatal error

节目输出

string(32) "me is the previous error handler"
array(4) {
  [0]=>
  int(8)
  [1]=>
  string(29) "Undefined variable: undefined"
  [2]=>
  string(45) "/tmp/execpad-0eca072b619d/source-0eca072b619d"
  [3]=>
  int(28)
}

Fatal error: Uncaught exception 'ErrorException' with message 'Argument 1 passed to {closure}() must be callable, object given, called in /tmp/execpad-0eca072b619d/source-0eca072b619d on line 30 and defined' in /tmp/execpad-0eca072b619d/source-0eca072b619d:26
Stack trace:
#0 /tmp/execpad-0eca072b619d/source-0eca072b619d(26): {closure}(4096, 'Argument 1 pass...', '/tmp/execpad-0e...', 26, Array)
#1 /tmp/execpad-0eca072b619d/source-0eca072b619d(30): {closure}(Object(stdClass))
#2 {main}
  thrown in /tmp/execpad-0eca072b619d/source-0eca072b619d on line 26

出色的写作。在测试期间,重新设置错误处理程序是否会导致性能明显下降?
mpw 2015年

我还没走 核心中还有一个相关区域,在开发模式下,所有警告/错误都会转换为Exception(而不是ErrorExceptuion-甚至不会记录)。这可能需要补丁集以理智的方式解决。对于错误处理程序,有没有好的分配方法可用,也是在这里我有点倾向于补丁核心甚至在带来固定的默认错误处理程序。
hakre

1

默认情况下,通过添加 (Exception $e)函数参数定义。

除了Exception或Exception的扩展,您不能将其他任何东西传递给此函数。


看一下mageCoreErrorHandler功能。由不正确的参数触发的错误将在非开发人员模式下处理和抑制,并Exception在开发人员模式下抛出。
mpw

首先发生这种情况是严重错误的。Magento mageCoreErrorHandler可以确保访问者不会在他们的面前抛出错误。您可以自己建立一个自己try{}catch(){}来抢夺它们,如果您不能将它们继续下去。
Jeroen 2015年

考虑到在抑制可触及的致命错误的情况下不会引发任何异常,try / catch将为我带来什么?
mpw

1
经过本地测试之后,我终于明白了。您说得对,错误被抑制了,代码将继续。我会更新我的答案,并添加一些额外的想法
吉荣

我将发布一个新答案,否则我们的对话将毫无意义
Jeroen 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.