1

カスタム エラー ハンドラに関連する Stackoverflow について、すでに多くの質問があることは承知しています。しかし、それらの多くと PHP のマニュアルを読んでも、まだ問題を解決できません。したがって、私はこの質問を投稿しています。

私のスクリプトは現在、次のように構成されています。

require 'file.php';
require 'anotherFile.php';
// several more "require" here. These files contain many functions

function myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext){
    // some code to handle errors here
    
}

class myObject {
    function __construct() {
        // set the values here
    }

    function computeSomething() {
        ...
        doFunction2();
        ...
    }

    function SomethingBadHappened()
    {
    }
}

function doFunction1() {
    // for some reason, an error happens here
    // it is properly handled by the current error handler
}

function doFunction2() {
    // for some reason, an error happens here
    // since it got called by $obj, I want the error handler to run $obj->SomethingBadHappened();
    // but $obj is not known in myErrorHandler function!
}

set_error_handler('myErrorHandler');

// some procedural code here
doFunction1();
doAnotherThing();

// then I use objects
$obj = new myObject();
$obj->run();

// then I may use procedural code again
doSomethingElse();

カスタム エラー ハンドラーは既に正常に動作しています。エラーハンドラーが設定された後に実行されるコードで発生するすべての PHP エラーをキャッチして処理します。

私の問題:

クラスのメソッド内でエラーが発生した場合myObject、非静的メソッドを呼び出したい:

$obj->SomethingBadHappened();

$objの範囲外ですmyErrorHandler$objエラーハンドラー内にアクセスしてメンバー関数を呼び出すにはどうすればよい$objですか?
現在、300KB の PHP コードがあり、すべての関数の署名を変更し$objてパラメーターとして追加することはできません (関数が多すぎます!)。

カスタムエラーハンドラーをオブジェクトのメソッドとして定義できることを読みました。myObjectしかし、そうすると、 ($obj)のインスタンスを作成する前に発生したエラーをキャッチできなくなります。

例外についても読みましたが、問題の解決には役立たないようです。グローバル変数を使用したくありません。グローバル変数を避けるべき理由を説明する 2 つの質問を以下に示します。

4

2 に答える 2

5

誰も飛びつきたがらないような興味深い質問なので、2 セント相当の金額を差し上げます。すべてが IMO によって認定されています ...

エラー処理とは、エラーをきちんと処理することであり、何らかの形でユーザー フレンドリーな方法でエンド ユーザーに報告することです。さらに、アプリケーション サポートのための内部フォレンジックを規定することもあります。一般に、アプリケーション コンテキストを公開または解釈しようとする必要があります。これは非常に困難であり、その性質上コンテキスト固有であるため、ワームの缶を開けてしまうことになります。

コンテキスト内でエラーを階層的に処理したい場合は、実際に例外と例外処理の世界に移動したことになり、これはまったく異なるランタイム モデルです。ここで、原則として、アプリケーションの例外を処理し、オブジェクト コンテキストを保持することができますが、これは実装が扱いにくいことが判明する可能性があり、ここで役立つ標準パターンは思い浮かびません。

シングルトンの場合$obj(そして、シングルトン テンプレートの使用を検討したい場合)、これにより、必要なことを行うことができます。

一方、「顧客レコード XXXX の処理中」などの限られたコンテキストを利用できるようにしたい場合は、ハンドラの静的メソッドと推定コンテキストを登録する別の登録メソッドを使用して、エラー ハンドラをクラスにカプセル化してみませんか。 、またはオブジェクト固有のメソッドを登録してこのコンテキストを提供できるようにするコールバックを登録するには? もちろん、潜在的なネストされたエラーを回避するために、クラス/オブジェクト関数を使用して、登録されたコールバックがまだ有効なスコープ内にあることを検証するように細心の注意を払う必要がありますが、この種のフレームワークを実行可能にすることができます。

Jocelyn のフィードバックに続く脚注

シングルトンの長所と短所の議論を理解しています。そのため、私はそれらを自分で使用せず、最初の回答でも非推奨にしました。ただし、注意して使用すると、いくつかの利点が得られます。そのため、MediaWiki エンジンなどのいくつかの最善の組み合わせのアプリケーションは、依然としてそれらを使用しています。PHP スコープの制約は依然として存在します。$someObject->method()別のクラスまたは関数から呼び出すには、呼び出し元の関数のスコープ内にある必要があります。これを回避する方法はありません。したがって、いくつかのオプションがあります。

  • アクティブなオブジェクトが常に1 つしかない場合someClassは、いくつかの方法でこれをグローバル スコープに配置できます。最も簡単な方法は、オブジェクトを何らかのグローバル変数にコピーするか、静的メソッドを使用してこれを返すことです (これらはすべて、someClass::get()とにかく、古典的なシングルトンメソッドはそうします。

  • 一方、スコープ内に推定される可能性のある複数のオブジェクトがある場合は、オブジェクトをパラメーターとして渡すことができますが、これを行う必要はまったくありません。これらをエラーハンドラーに登録するプッシュメソッドを使用できます。実装例の 1 つは、4 つの静的メソッドと静的プライベート変数を使用してエラー ハンドラー クラスを作成することです。

    • myErrorHandler() . あなたが説明したエラーハンドラーですが、現在は静的クラスメソッドです。

    • initErrorHandler() . これを呼び出して、上記の静的メソッドを登録します。

    • $ callback は標準array($obj,'method')パラメーターです。

    • unregisterClassCallback($callback)、ここで $callback は同じarray($obj,'method')パラメーターです。

    • $registeredCallbacks . registerClassCallback()追加およびunregisterClassCallback()削除する登録済みコールバックのプライベート配列。を使用しmyErrorHandler()てこれに対して foreach を実行するだけで、登録された各コールバックがまだスコープ内にあることを確認してから呼び出すことができます。class_exists()method_exists()

これはあなたが求めていることをします。PHP では、変数へのオブジェクトの代入は事実上オブジェクトへのハンドルであることを思い出してください。オブジェクト (ハンドル) を任意の変数コンテキストにコピーできます。これは、基になるオブジェクトをコピーまたはディープ コピーしません。これは事実上参照です (ただし、真の PHP オブジェクト参照とは微妙に意味が異なりますが、それは別の Q&A です)。

于 2012-09-10T12:11:21.783 に答える
1

私は最終的にこのデザインに決めました:

  • 現在のエラー ハンドラは変更されません (function myErrorHandler)。エラーが発生したときに実行されるコードを保持します (コード内の任意の場所)
  • クラスmyObjectに別のエラーハンドラーを追加しました(関数myObject_error_handler
  • myObject新しいエラーハンドラを登録するコンストラクタ
  • 関数myObject_error_handlerは関数SomethingBadHappenedを呼び出しmyErrorHandlerてから、エラーを処理するために呼び出すことができます

そうすることの主な利点は、既存のコードにほとんど変更を加える必要がないことです。

  • 私のクラスの新しいメソッド
  • コンストラクターに新しいエラーハンドラーを登録する
  • 新しいエラー ハンドラから古いエラー ハンドラへの呼び出し

新しいコードは次のとおりです。

require 'file.php';
require 'anotherFile.php';
// several more "require" here. These files contain many functions

function myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
    // some code to handle errors here

}

class myObject {
    function __construct() {
        // set the values here

        // register another error handler
        set_error_handler(array($this, 'myObject_error_handler'));
    }

    function myObject_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
        // call any number of methods of the current class
        $this->SomethingBadHappened();

        // then call the other error handler
        myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext);
    }

    function computeSomething() {
        ...
        doFunction2();
        ...
    }

    function SomethingBadHappened() {
        // does something when an error happens
    }
}

function doFunction1() {
    // for some reason, an error happens here
    // it is properly handled by the current error handler
}

function doFunction2() {
    // for some reason, an error happens here
    // now the function myObject_error_handler will be called automatically
}

set_error_handler('myErrorHandler');

// some procedural code here
doFunction1();
doAnotherThing();

// then I use objects
$obj = new myObject();
$obj->run();

// then I may use procedural code again
set_error_handler('myErrorHandler');
doSomethingElse();
于 2012-09-29T17:00:37.127 に答える