web-dev-qa-db-ja.com

ユーザー定義オブジェクトの型キャスト

__ToStringで行うように、キャスト用のメソッドを定義する方法はありますか?

$obj = (MyClass) $another_class_obj;
50
Arsham

PHPでキャストを入力する必要はありません。


編集:このトピックは混乱を招くように思われるので、少し詳しく説明したいと思いました。

Javaなどの言語では、型を伝える可能性のあるものが2つあります。コンパイラには型に関する概念があり、ランタイムには型に関する別の考えがあります。コンパイラのタイプは変数に関連付けられていますが、ランタイムエンジンは値のタイプを追跡します(変数に割り当てられます)。変数の型はコンパイル時に認識されますが、値の型は実行時にのみ認識されます。

入力コードの一部がコンパイラの型システムに違反する場合、コンパイラはコンパイルを中断して停止します。言い換えれば、静的型システムに違反するコードをコンパイルすることは不可能です。これは、特定のクラスのエラーをキャッチします。たとえば、次の(簡略化された)Javaコード:

class Alpha {}

class Beta extends Alpha {
  public void sayHello() {
    System.out.println("Hello");
  }
}

今これをした場合:

Alpha a = new Beta();

BetaAlphaのサブクラスであり、したがってa型の変数Alphaの有効な値なので、問題ありません。ただし、続行する場合:

a.sayHello();

メソッドsayHelloAlphaの有効なメソッドではないため、コンパイラーはエラーを返します-aが実際にBetaであることがわかっていても。

型キャストを入力してください:

((Beta) a).sayHello();

ここでは、変数a(この場合)をBetaとして扱うようコンパイラーに指示します。これは型キャストとして知られています。この抜け穴は、言語の多相性を可能にするため非常に便利ですが、明らかに、型システムのあらゆる種類の違反の裏口でもあります。したがって、何らかの型安全性を維持するために、いくつかの制限があります。関連するタイプにのみキャストできます。例えば。階層を上下します。つまり、完全に無関係なクラスCharlieにキャストすることはできません。

これはすべてコンパイラで発生することに注意することが重要です-つまり、コードが実行される前に発生します。 Javaは、実行時タイプのエラーを発生させる可能性があります。たとえば、次の場合:

class Alpha {}

class Beta extends Alpha {
  public void sayHello() {
    System.out.println("Hello");
  }
}

class Charlie extends Alpha {}

Alpha a = new Charlie();
((Beta) a).sayHello();

上記のコードはコンパイラに有効ですが、BetaからCharlieへのキャストには互換性がないため、実行時に例外が発生します。

一方、PHPファームに戻ります。

以下は、PHPコンパイラーに有効です-これを実行可能なバイトコードに変換しますが、実行時エラーが発生します。

class Alpha {}

class Beta extends Alpha {
  function sayHello() {
    print "Hello";
  }
}
$a = new Alpha();
$a->sayHello();

これは、PHP変数には型がないためです。コンパイラーは、変数に対してどの実行時型が有効であるかを認識していないため、強制しようとしません。 Javaのいずれかのように型を明示的に指定します。型ヒントはありますが、はい、これらは単にランタイムコントラクトです。以下は引き続き有効です。

// reuse the classes from above
function tellToSayHello(Alpha $a) {
  $a->sayHello();
}
tellToSayHello(new Beta());

PHPvariablesには型はありませんが、valuesそれでもありますPHPの特に興味深い側面は、値のタイプを変更できることです。

// The variable $foo holds a value with the type of string
$foo = "42";
echo gettype($foo); // Yields "string"
// Here we change the type from string -> integer
settype($foo, "integer");
echo gettype($foo); // Yields "integer"

この機能は型キャストと混同される場合がありますが、これは誤った呼び名です。型は依然として値のプロパティであり、型の変更はコンパイル時ではなく実行時に行われます。

PHPでは、型を変更する機能もかなり制限されています。タイプを変更できるのは、オブジェクトではなく単純タイプ間のみです。したがって、型をあるクラスから別のクラスに変更することはできません。新しいオブジェクトを作成して状態をコピーできますが、タイプを変更することはできません。 PHPは、この点で少し部外者です。他の同様の言語は、クラスをPHPよりもはるかに動的な概念として扱います。

PHPの別の同様の機能は、次のように値を新しい型として複製できることです。

// The variable $foo holds a value with the type of string
$foo = "42";
echo gettype($foo); // Yields "string"
// Here we change the type from string -> integer
$bar = (integer) $foo;
echo gettype($bar); // Yields "integer"

構文的には、これは型キャストが静的に型付けされた言語で書かれている方法によく似ています。そのため、実行時の型変換であっても、型キャストと混同されることもよくあります。

要約:型キャストは、変数の型を変更する操作です(not値)。 PHPでは変数は型を持たないため、実行することは不可能であるだけでなく、そもそも質問するのは無意味なことです。

101
troelskn

PHPで型キャストする必要はありませんが、親オブジェクトを子オブジェクトに変換したい場合があります。

シンプル

//Example of a sub class
class YourObject extends MyObject {
    public function __construct(MyObject $object) {
        foreach($object as $property => $value) {
            $this->$property = $value;
        }
    }
}


$my_object = new MyObject();
$your_object = new YourObject($my_object);

そのため、親オブジェクトを子オブジェクトのコンストラクターに渡し、コンストラクターにプロパティをコピーさせるだけです。必要に応じてフィルタリング/変更することもできます。

高度な

//Class to return standard objects
class Factory {
    public static function getObject() {
        $object = new MyObject();
        return $object;
    }
}

//Class to return different object depending on the type property
class SubFactory extends Factory {
    public static function getObject() {
        $object = parent::getObject();
        switch($object->type) {
        case 'yours':
            $object = new YourObject($object);
            break;
        case 'ours':
            $object = new OurObject($object);
            break;
        }
        return $object;
    }
}

//Example of a sub class
class YourObject extends MyObject {
    public function __construct(MyObject $object) {
        foreach($object as $property => $value) {
            $this->$property = $value;
        }
    }
}

型キャストではありませんが、必要なことは行います。

8
Jrgns

オブジェクトのクラスを変更する関数は次のとおりです。

/**
 * Change the class of an object
 *
 * @param object $obj
 * @param string $class_type
 * @author toma at smartsemantics dot com
 * @see http://www.php.net/manual/en/language.types.type-juggling.php#50791
 */
function changeClass(&$obj,$new_class)
{
    if(class_exists($class_type,true))
    {
        $obj = unserialize(preg_replace("/^O:[0-9]+:\"[^\"]+\":/i",
            "O:".strlen($class_type).":\"".$new_class."\":", serialize($obj)));
    }
}

明確ではない場合、これは私の機能ではなく、 http://www.php.net/manual/en/language.types.typeの「toma at smartsemantics dot com」による投稿から引用したものです。 -juggling.php#50791

7
Josh

Joshが投稿した関数を修正しました(未定義の$ new_class変数によりエラーになります)。ここに私が得たものがあります:

function changeClass(&$obj, $newClass)
{   $obj = unserialize(preg_replace // change object into type $new_class
    (   "/^O:[0-9]+:\"[^\"]+\":/i", 
        "O:".strlen($newClass).":\"".$newClass."\":", 
        serialize($obj)
    ));
}

function classCast_callMethod(&$obj, $newClass, $methodName, $methodArgs=array())
{   $oldClass = get_class($obj);
    changeClass($obj, $newClass);

    // get result of method call
    $result = call_user_func_array(array($obj, $methodName), $methodArgs);
    changeClass(&$obj, $oldClass);  // change back
    return $result;
}

クラスキャストが機能すると期待するのと同じように機能します。クラスメンバーにアクセスするために似たようなものを作成できますが、私はそれが必要になるとは思わないので、他の人に任せます。

「phpはキャストしません」または「phpでキャストする必要はありません」と言うすべてのジャークにブーイング。ブルホッケー。キャストはオブジェクト指向の生活の重要な部分であり、いシリアル化ハックよりも良い方法を見つけられるといいのですが。

ジョシュありがとう!

7
B T

タイプヒンティングのためのキャストが目的であれば、これは機能します。

if( is_object($dum_class_u_want) && $dum_class_u_want instanceof ClassYouWant )
{
    // type hints working now
    $dum_class_u_want->is_smart_now();
}

うん。

1
DontBreakIt

PHPにそれを処理するオーバーロード演算子があるとは思わないが、

<?php

class MyClass {

  protected $_number;

  static public function castFrom($obj) {
    $new = new self();
    if (is_int($obj)) {
      $new->_number = $obj;
    } else if ($obj instanceOf MyNumberClass){
      /// some other type of casting
    }
    return $new;
  }
}

$test = MyClass::castFrom(123123);
var_dump($test);

それを処理する1つの方法です。

1
gnarf

より良いIDEを作成するには、型キャストが必要だと思います。しかし、PHP言語自体は型キャストを必要としませんが、変数の値に対するランタイム型の変更をサポートします。オートボクシングとアンボクシングをご覧ください。これは、PHPが本質的に行うことです。申し訳ありませんが、すでにIDEの方が優れています。

1

Type-Hintingを意味すると思います。

PHP 7.2の時点で、関数で引数を入力することができます:

function something(Some_Object $argument) {...} # Type-hinting object on function arguments works on PHP 7.2+

ただし、次のように入力することはできません。

(Some_Object) $variable = get_some_object($id); # This does not work, even in PHP 7.2

PHPで公式に実装されていない場合の型ヒントオブジェクトの代替は、次のとおりです。

$variable = get_some_object($id); # We expect Some_Object to return
is_a($argument, 'Some_Object') || die('get_some_object() function didn't return Some_Object');
0

PHP 7.2

class One
{
    protected $one = 'one';
}

class Two extends One
{
    public function funcOne()
    {
        echo $this->one;
    }
}


    $foo = new One();
    $foo = (Two) $foo;
    $foo->funcOne();

このようなエラーが発生します

PHP解析エラー:構文エラー、予期しない '$ foo'(T_VARIABLE)、xxx.php行xxx

あなたは基本的にそれを行うことはできませんが、もう一度考えてみてください、あなたはクラスの他の公開機能の上に新しい関数だけが欲しかったのでしょうか?

Wrapperを使用してそれを行うことができます

class One
{
    protected $one;
    public function __construct(string $one)
    {
        $this->one = $one;
    }
    public function pub(string $par){
        echo (__CLASS__ . ' - ' . __FUNCTION__ . ' - ' . $this->one . '-' .$par);
    }
}

class Wrapper
{
    private $obj;

    public function __construct(One $obj)
    {
        $this->obj = $obj;
    }
    public function newFunction()
    {
        echo (__CLASS__ . ' - ' . __FUNCTION__);
    }
    public function __call($name, $arguments)
    {
        return call_user_func_array([$this->obj, $name], $arguments);
    }
}

    $foo = new One('one');
    $foo->pub('par1');
    $foo = new Wrapper($foo);
    $foo->pub('par2');
    $foo->newFunction();

1-パブ-one-par1

One-パブ-one-par2

ラッパー-newFunction

保護されたプロパティを取得したい場合はどうしますか?

それもできます

class One
{
    protected $one;

    public function __construct(string $one)
    {
        $this->one = $one;
    }
}


    $foo = new One('one');
    $tmp = (new class ($foo) extends One {
            protected $obj;
            public function __construct(One $obj)
            {
                $this->obj = $obj;
                parent::__construct('two');
            }
            public function getProtectedOut()
            {
                return $this->obj->one;
            }
        } )->getProtectedOut();

    echo ($tmp);

あなたが得る

1

そして、あなたは同じようにget_protected_inすることができます

0