では、すべてのメソッドを保護または非公開にするとどうなるでしょうか? (私はこれが古くて「答えられた」質問であることを知っています)
__call マジック メソッドは、存在しない非パブリック メソッドをすべてインターセプトするため、すべてのメソッドをパブリックにしないと、それらすべてをインターセプトできます。
public function __call( $func, $args )
{
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
Handle::eachMethodAction(); // action which will be fired each time a method will be called
return $this->$func( ...$args );
}
そのおかげで、コードに何もする必要がなくなり ( __call を追加して quick を実行することを期待してreplace all
ください)、クラスに共通の親がある場合は、それを親に追加するだけで、もう気にする必要はありません。
しかし
このソリューションは、次の 2 つの大きな問題を引き起こします。
- 保護された/プライベート メソッドは自動的に公開されます
- エラーは、適切なファイルではなく __call を指しています
私たちは何ができる?
カスタムプライベート/保護
すべてのプロテクト/プライベート メソッドのリストを追加し、呼び出しの前にメソッドをパブリックに戻すことができるかどうかを確認できます。
public function __call( $func, $args )
{
$private = [
"PrivateMethod" => null
];
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
if ( isset( $private[$func] ) ) throw new Error("This method is private and cannot be called");
Handle::eachMethodAction(); // action which will be fired each time a method will be called
return $this->$func( ...$args );
}
多くの人にとって、これは契約を破る可能性がありますが、私は個人的にこのアプローチを、パブリック メソッドのみを持つクラス (保護するように設定) でのみ使用します。publicClass
したがって、可能であれば、メソッドをおよびに分離してprivateClass
、この問題を解消することができます。
カスタム エラーとスタック
より良いエラーのために、私はこのメソッドを作成しました:
/**
* Get parent function/method details
*
* @param int counter [OPT] The counter allows to move further back or forth in search of methods detalis
*
* @return array trace It contains those elements :
* - function - name of the function
* - file - in which file exception happend
* - line - on which line
* - class - in which class
* - type - how it was called
* - args - arguments passed to function/method
*/
protected function getParentMethod( int $counter = 0 ) {
$excep = new \Exception();
$trace = $excep->getTrace();
$offset = 1;
if ( sizeof( $trace ) < 2 ) $offset = sizeof( $trace ) - 1;
return $trace[$offset - $counter];
}
保護されたメソッドを呼び出した前のメソッド/関数に関する詳細を返します。
public function __call( $func, $args )
{
$private = [
"PrivateMethod" => null
];
if ( !method_exists( $this, $func ) ) {
$details = (object) $this->getParentMethod();
throw new Error("Method $func does not exist on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
}
if ( isset($private[$func]) ) {
$details = (object) $this->getParentMethod();
throw new Error("Method $func is private and cannot be called on line " . $details->line . ", file: " . $details->file . " invoked by " . get_class($this) . $details->type . $func . " () ");
}
return $this->$func( ...$args );
}
これは大きな問題ではありませんが、デバッグ中に混乱を招く可能性があります。
結論
このソリューションにより、クラス外からのプライベート/保護されたメソッドの呼び出しを制御できます。Anythis->Method
は省略__call
し、通常は private/protected メソッドを呼び出します。
class Test {
public function __call( $func, $args )
{
echo "__call! ";
if ( !method_exists( $this, $func ) ) throw new Error("This method does not exist in this class.");
return $this->$func( ...$args );
}
protected function Public()
{
return "Public";
}
protected function CallPublic()
{
return "Call->" . $this->Public();
}
}
$_Test = new Test();
echo $_Test->CallPublic(); // result: __call! Call->Public - it uses two methods but __call is fired only once
静的メソッドに同様のものを追加する場合は、__callStatic
マジック メソッドを使用します。