44

クラスFooのオブジェクトがあります:

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

$obj = new Foo();

私がしたい (そしてしなければならない) ことは、次のように、このオブジェクトを配列にキャストすることです。

$arr = (array)$obj;

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

4

6 に答える 6

46

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

于 2012-06-20T14:18:15.377 に答える
15

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

$s = (string)$obj;

どちらがメソッドをトリガー__toString()し、どちらをオーバーライドできますか。

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

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

于 2012-06-20T14:22:53.783 に答える
4

この質問がまだ関連しているかどうかはわかりませんが、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 はArrayObject::STD_PROP_LIST、デフォルトとArrayObject::ARRAY_AS_PROPS代替として 2 つのフラグを受け入れます。

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

$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)
 */
于 2016-12-28T16:41:31.740 に答える
2

元のクラス定義を変更せずにこれを行う 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"
  }
}
于 2012-09-17T14:31:31.277 に答える