web-dev-qa-db-ja.com

オブジェクトを配列にキャストしています-呼び出されている魔法のメソッドはありますか?

私はクラスFooのオブジェクトを持っています:

_class Foo extends Bar {
    protected $a;
    protected $b;
}

$obj = new Foo();
_

私がやりたいこと(そして持っていること)は、このオブジェクトを次のように配列にキャストすることです:

_$arr = (array)$obj;
_

この時点で呼び出されているマジック(またはマジックではない)メソッドはありますか?それとも傍受する他の方法はありますか?簡単なメソッドを書くことができることは知っています。 asArray()はFooにありますが、「ネイティブ」な方法を探していますPHP方法。

38
Majql

クラスに ArrayAccess インターフェースを実装させることができます。これにより、キャストせずにオブジェクトを配列のように扱うことができ、メンバーの使用方法を完全に制御できます。

37
Explosion Pills

番号

PHPには__toArrayマジックメソッドはありません。 拡張提案は拒否されました2006で、次の回答が返されます。

[2006-08-20 11:12 UTC] [email protected]

なぜ単にインターフェースの一部としてasArray()メソッドを持たないのですか?

インターフェイスArrayConversion {function asArray(); }

参照してください。echo、print、その他の内部関数などの言語構造でサポートされている__toStringがあります。しかし、配列の自動変換はすでに反対に決定しました。したがって、どの言語構成でもサポートされることはありません。とはいえ、これに必要なものはなく、上記のインターフェースに対して勝つことは何もありません。実際、魔法の機能をもう1つ追加するだけなので、phpをより複雑にするでしょう。

したがって、将来のリリースで実装される可能性は非常に低いです(私に尋ねれば残念ですが)。

45
tacone

悲しいことに、配列にキャストしても、次のように魔法のメソッドはトリガーされません。

_$s = (string)$obj;
_

__toString()メソッドをトリガーし、オーバーライドできるもの。

ただし、カスタムtoArray()メソッドを記述することもできます。

また、カスタムシリアライザー戦略を記述できる Serializable インターフェイスにも興味があるかもしれません。

14
Boris Guéry

この質問がまだ関連しているかどうかはわかりませんが、phpには組み込みの ArrayObject クラスがあり、オブジェクトを配列として扱うことができ、データベースレコードまたはコレクションのコンテナーとして使用すると便利です。

厳密な型に関してはベストプラクティスではないかもしれませんが、オブジェクトを配列として扱うことができ、両方のステートメントが有効です。

_$obj = new ArrayObject(['a' => 'alpha']);
var_dump($obj['a']); //alpha
var_dump($obj->getOffset('a'));//alpha
_

ただし、ArrayObjectの動作を覚えておく必要があります

_$obj = new ArrayObject(['a' => 'alpha']);
//Access Property
var_dump($obj['a']); //alpha
var_dump($obj->offsetGet('a'));//alpha
var_dump($obj->a); //null Notice: Undefined property: ArrayObject::$a

//Serialization
var_dump(serialize($obj));// string 'C:11:"ArrayObject":41:{x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}}' (length=65)
var_dump($obj->serialize());// string 'x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}'
var_dump(serialize($obj) === $obj->serialize());// false !!!

//Setting Properties
$obj['b'] = 'beta'; //OK
$obj->c = 'gamma'; //value becomes object property!!!
var_dump($obj);
/* OBJECT DUMP
object(ArrayObject)[13]
  public 'c' => string 'gamma' (length=5)
  private 'storage' =>
    array (size=2)
      'a' => string 'alpha' (length=5)
      'b' => string 'beta' (length=4)
 */

//Property validation as array
var_dump(isset($obj['a']));//true
var_dump(isset($obj['b']));//true
var_dump(isset($obj['c']));//false
//Property validation as object
var_dump(isset($obj->a));//false
var_dump(isset($obj->b));//false
var_dump(isset($obj->c));//true

//Typecasting
var_dump((array)$obj);
/*
array (size=2)
  'a' => string 'alpha' (length=5)
  'b' => string 'beta' (length=4)
 */

//var_dump((string)$obj);// Catchable fatal error: Object of class ArrayObject could not be converted to string
_

ArrayObjectは、2つのフラグ_ArrayObject::STD_PROP_LIST_をデフォルトとして受け入れ、_ArrayObject::ARRAY_AS_PROPS_を代替として受け入れます。

これにより、値を読み取る動作が変更されますが、その方法での新しいプロパティの設定はサポートされていません。次に例を示します。

_$obj = new ArrayObject(['a' => 'alpha'], ArrayObject::ARRAY_AS_PROPS);
//Access Property
var_dump($obj['a']); //alpha
var_dump($obj->offsetGet('a'));//alpha
var_dump($obj->a);//alpha

//Serialization
var_dump(serialize($obj));// string 'C:11:"ArrayObject":41:{x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}}' (length=65)
var_dump($obj->serialize());// string 'x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}'
var_dump(serialize($obj) === $obj->serialize());// false !!!

//Setting Properties
$obj['b'] = 'beta'; //OK
$obj->c = 'gamma'; //OK
var_dump($obj);
/* OBJECT DUMP
object(ArrayObject)[14]
  private 'storage' =>
    array (size=3)
      'a' => string 'alpha' (length=5)
      'b' => string 'beta' (length=4)
      'c' => string 'gamma' (length=5)
 */

//Property validation as array
var_dump(isset($obj['a']));//true
var_dump(isset($obj['b']));//true
var_dump(isset($obj['c']));//false !!!
//Property validation as object
var_dump(isset($obj->a));//true
var_dump(isset($obj->b));//true
var_dump(isset($obj->c));//true

//Typecasting
var_dump((array)$obj);
/*
array (size=2)
  'a' => string 'alpha' (length=5)
  'b' => string 'beta' (length=4)
 */
_

この動作の一貫性を高めるには、このクラスを拡張し、マジックメソッド__get()__set()__isset()および__unset()を実装する必要があります。

別のトリッキーな部分はシリアル化です。デフォルトのメソッドserializeは、オブジェクト自体ではなく、シリアル化された_$storage_変数のコピーを返します。インスタンスのシリアル化されたコピーを返す回避策として、___toString_にデフォルトのシリアル化を実装できますメソッド、このように正しく動作します。

_class FooObject extends ArrayObject
{
    public function __get($index)
    {
        if ($this->offsetExists($index)) {
            return $this->offsetGet($index);
        } else {
            throw new UnexpectedValueException('Undefined key ' . $index);
        }
    }

    public function __set($index, $value)
    {
        $this->offsetSet($index, $value);
        return $this;
    }

    public function __isset($index)
    {
        return $this->offsetExists($index);
    }

    public function __unset($index)
    {
        return $this->offsetUnset($index);
    }

    public function __toString()
    {
        return serialize($this);
    }
}
_

使用例

_$obj2 = new FooObject(['a' => 'alpha']);
//Access Property
var_dump($obj2['a']); //alpha
var_dump($obj2->offsetGet('a'));//alpha
var_dump($obj2->a); //alpha

//Serialization
var_dump(serialize($obj));// string 'C:11:"ArrayObject":41:{x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}}' (length=65)
var_dump($obj->serialize());// string 'x:i:0;a:1:{s:1:"a";s:5:"alpha";};m:a:0:{}'
var_dump(serialize($obj) === $obj->serialize());// false !!!

//Setting Properties
$obj2['b'] = 'beta'; //OK
$obj2->c = 'gamma'; //OK
var_dump($obj2);
/* OBJECT DUMP
object(FooObject)[14]
  private 'storage' (ArrayObject) =>
    array (size=3)
      'a' => string 'alpha' (length=5)
      'b' => string 'beta' (length=4)
      'c' => string 'gamma' (length=5)
 */

//Property validation as array
var_dump(isset($obj2['a']));//true
var_dump(isset($obj2['b']));//true
var_dump(isset($obj2['c']));//true
//Property validation as object
var_dump(isset($obj2->a));//true
var_dump(isset($obj2->b));//true
var_dump(isset($obj2->c));//true

//Typecasting
var_dump((array)$obj2);
/*
array (size=3)
  'a' => string 'alpha' (length=5)
  'b' => string 'beta' (length=4)
  'c' => string 'gamma' (length=5)
 */
_
3
Nazariy

元のクラス定義を変更せずにこれを行う1つの方法は、リフレクションを使用することです。これにより、実行時にクラスのプロパティを調べることができます。

マニュアルから取得: http://www.php.net/manual/en/reflectionclass.getproperties.php

<?php
class Foo {
    public    $foo  = 1;
    protected $bar  = 2;
    private   $baz  = 3;
}

$foo = new Foo();

$reflect = new ReflectionClass($foo);
$props   = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);

foreach ($props as $prop) {
    print $prop->getName() . "\n";
}

var_dump($props);

?>

The above example will output something similar to:
foo
bar
array(2) {
  [0]=>
  object(ReflectionProperty)#3 (2) {
    ["name"]=>
    string(3) "foo"
    ["class"]=>
    string(3) "Foo"
  }
  [1]=>
  object(ReflectionProperty)#4 (2) {
    ["name"]=>
    string(3) "bar"
    ["class"]=>
    string(3) "Foo"
  }
}
2
hessodreamy

コンテキストからアクセス可能なすべてのプロパティ名/値の連想配列を返すget_object_vars($ yourObject)を使用できます。

参照 http://php.net/manual/en/function.get-object-vars.php

保護されたプロパティまたはプライベートプロパティにアクセスしたい場合は、メソッドgetArrayCopy()を実装するArrayObjectを拡張することをお勧めします

1
Aghanim