web-dev-qa-db-ja.com

PHP)のクラスの外部からプライベートメソッドとプライベートプロパティを呼び出す

非常にまれな特定のケースで、クラスの外部からプライベートメソッドと変数にアクセスしたいと思います。

イントロスペクションは使用されていますが、これは不可能であることがわかりました。

具体的なケースは次のケースです。

私はこのようなものが欲しいです:

class Console
{
    final public static function run() {

        while (TRUE != FALSE) {
            echo "\n> ";
            $command = trim(fgets(STDIN));

            switch ($command) {
                case 'exit':
                case 'q':
                case 'quit':
                    echo "OK+\n";
                    return;
                default:
                    ob_start();
                    eval($command);
                    $out = ob_get_contents();
                    ob_end_clean();

                    print("Command: $command");
                    print("Output:\n$out");         

                    break;
            }
        }
    }
}

このメソッドは、次のようにコードに挿入できるはずです。

Class Demo
{
    private $a;

    final public function myMethod()
    {
        // some code
        Console::run();
        // some other code
    }

    final public function myPublicMethod()
    {
        return "I can run through eval()";
    }

    private function myPrivateMethod()
    {
        return "I cannot run through eval()";
    }
}

(これは1つの単純化にすぎません。実際の単純化はソケットを通過し、さらに多くのものを実装します...)

そう...

クラスDemoをインスタンス化し、$ demo-> myMethod()を呼び出すと、コンソールが表示されます。そのコンソールは、次のようなコマンドを記述して最初のメソッドにアクセスできます。

> $this->myPublicMethod();

しかし、2番目のものを正常に実行することはできません。

> $this->myPrivateMethod();

何かアイデアがありますか、またはPHP)用のライブラリがありますか?

どうもありがとう!

17

メソッドを公開するだけです。しかし、トリッキーになりたい場合は、これを試すことができます(PHP 5.3):

class LockedGate
{
    private function open()
    {
        return 'how did you get in here?!!';
    }
}

$object = new LockedGate();
$reflector = new ReflectionObject($object);
$method = $reflector->getMethod('open');
$method->setAccessible(true);
echo $method->invoke($object);
59
webbiedave

EDIT:パラメーターを使用したプライベート関数呼び出しの例を含むように更新されました。

PHP 5.4以降、事前定義されたClosureクラスを使用して、クラスのメソッド/プロパティを、プライベートメンバーにもアクセスできるデルタ関数にバインドできます。

クロージャクラス

たとえば、プライベート変数を持つクラスがあり、クラスの外部でそれにアクセスしたいとします。

class Foo {
    private $bar = "Foo::Bar";
    private function add_ab($a, $b) {
        return $a + $b;
    }
}

PHP 5.4 +

$foo = new Foo;

// Single variable example
$getFooBarCallback = function() {
    return $this->bar;
};

$getFooBar = $getFooBarCallback->bindTo($foo, 'Foo');

echo $getFooBar(); // Prints Foo::Bar

// Function call with parameters example
$getFooAddABCallback = function() {
    // As of PHP 5.6 we can use $this->fn(...func_get_args()) instead of call_user_func_array
    return call_user_func_array(array($this, 'add_ab'), func_get_args());
};

$getFooAddAB = $getFooAddABCallback->bindTo($foo, 'Foo');

echo $getFooAddAB(33, 6); // Prints 39

PHP 7の時点で、新しいClosure::callメソッドを使用して、プライベートメンバーの場合でも、オブジェクトの任意のメソッド/プロパティをコールバック関数にバインドできます。

PHP 7 +

$foo = new Foo;

// Single variable example
$getFooBar = function() {
    return $this->bar;
};

echo $getFooBar->call($foo); // Prints Foo::Bar

// Function call with parameters example
$getFooAddAB = function() {
    return $this->add_ab(...func_get_args());
};

echo $getFooAddAB->call($foo, 33, 6); // Prints 39
16
Christos Lytras

最初に尋ねる必要があるのは、クラスの外部からアクセスする必要がある場合、なぜプライベートと宣言したのかということです。それがあなたのコードでない場合、発信者はおそらくそれをプライベートと宣言する正当な理由があり、直接アクセスすることは非常に悪い(そしてほとんど維持できない)慣行です。

[〜#〜] edit [〜#〜]:Adam V.がコメントで指摘しているように、呼び出す前にプライベートメソッドにアクセスできるようにする必要がありますそれ。これを含むようにコードサンプルが更新されました。しかし、私はそれをテストしていません-答えを最新の状態に保つためにここに追加するだけです。

そうは言っても、 Reflection を使用してこれを実現できます。 ReflectionClassをインスタンス化し、呼び出したいメソッドに対して getMethod を呼び出してから、返された-に対して invoke を呼び出します。 ReflectionMethod

コードサンプル(私はテストしていないので、エラーが発生する可能性があります)は次のようになります。

$demo = new Demo();
$reflection_class = new ReflectionClass("Demo");
$reflection_method = $reflection_class->getMethod("myPrivateMethod");
$reflection_method->setAccessible(true);
$result = $reflection_method->invoke($demo, NULL);
4
Dathan

このような呼び出しを1行で行うために使用できる他の回答のバリエーションを次に示します。

public function callPrivateMethod($object, $methodName)
{
    $reflectionClass = new \ReflectionClass($object);
    $reflectionMethod = $reflectionClass->getMethod($methodName);
    $reflectionMethod->setAccessible(true);

    $params = array_slice(func_get_args(), 2); //get all the parameters after $methodName
    return $reflectionMethod->invokeArgs($object, $params);
}
3
Mark McEver

私は時々これらの問題を抱えていますが、コーディング標準を通してそれを回避しています。プライベート関数または保護された関数は、接頭辞アンダースコアで示されます。

private function _myPrivateMethod()

次に、関数を公開します。

public function _myPrivateMethod()

したがって、関数はパブリックですが、命名規則により、パブリックはプライベートであり、実際には使用すべきではないことが通知されます。

2
buggedcom

答えはメソッドに公開されます。あなたがやろうとしているどんなトリックでも、それは仲間の開発者には理解できないでしょう。たとえば、他のコードで、Demoクラスを見て、この関数がパブリックとしてアクセスされていることを知りません。

もう一つ。 そのコンソールは、次のようなコマンドを記述して最初のメソッドにアクセスできます:。どうすればこれが可能でしょうか?コンソールは、$ thisを使用してデモクラス関数にアクセスできません。

1
siva kiran

本当にいくつかのプライベートメソッドを実行したい場合は、reflectionClassが唯一の選択肢だと思います。とにかく、プライベートまたは保護されたプロパティへの読み取りアクセスが必要な場合は、次のコードを使用できます。

<?php
class Demo
{
    private $foo = "bar";
}

$demo = new Demo();

// Will return an object with public, private and protected properties in public scope.
$properties = json_decode(preg_replace('/\\\\u([0-9a-f]{4})|'.get_class($demo).'/i', '', json_encode((array) $demo)));

?>
0
seorch.me

メソッドが定義されているクラスにメソッドを追加できる場合は、call_user_method()を内部で使用するメソッドを追加できます。これはPHP 5.2.x

<?php
class SomeClass {
    public function callprivate($methodName) {
         call_user_method(array($this, $methodName));
    }

    private function somePrivateMethod() {
         echo 'test';
    }
}


$object = new SomeClass();
$object->callprivate('somePrivateMethod');
0
Thomas Hunziker

なぜ保護を使用しないのですか?そしてそれを拡張する

0
kobbycoder