web-dev-qa-db-fra.com

Lancer un objet dans un tableau - toute méthode magique appelée?

J'ai un objet de classe Foo:

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

$obj = new Foo();

Ce que je veux (et dois) faire, c'est convertir cet objet en un tableau, comme ceci:

$arr = (array)$obj;

Y a-t-il une méthode magique (ou pas magique :)) qui est appelée en ce moment? Ou existe-t-il un autre moyen de l'intercepter? Je sais que je peux écrire une méthode simple, par exemple. asArray() dans Foo, mais je cherche des moyens plus "natifs" PHP.

38
Majql

Vous pouvez demander à la classe d'implémenter l'interface ArrayAccess . Cela vous permettra de traiter l'objet comme un tableau sans transtypage et vous aurez un contrôle total sur la façon dont les membres sont utilisés.

37
Explosion Pills

Non

Il n'y a pas __toArray méthode magique en PHP. Un la proposition d'amélioration a été rejetée en 2006 avec la réponse suivante:

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

Pourquoi ne pas simplement avoir une méthode asArray () peut-être même comme pair d'une interface:

interface ArrayConversion {fonction asArray (); }

Voir, nous avons __toString car il est pris en charge dans les constructions de langage comme echo, print et d'autres fonctions internes. Mais nous avons déjà décidé de ne pas procéder à une autoconversion pour les tableaux. Il ne sera donc jamais pris en charge dans aucune construction de langage. Cela dit, cela n'est pas nécessaire et vous ne gagnerez rien contre l'interface ci-dessus. En fait, vous rendriez le php plus complexe car vous n'ajouteriez qu'une seule fonctionnalité magique.

Il est donc très peu probable qu'il soit implémenté dans une future version (ce qui est dommage, si vous me le demandez).

45
tacone

Malheureusement non, la conversion en tableau ne déclenche aucune méthode magique comme cela se fait avec:

$s = (string)$obj;

qui déclenche la méthode __toString() et que vous pouvez remplacer.

Cependant, vous pouvez écrire une méthode toArray() personnalisée.

Vous pouvez également être intéressé par l'interface Serializable qui vous permet d'écrire une stratégie de sérialisation personnalisée.

14
Boris Guéry

Je ne sais pas si cette question est toujours pertinente, mais php a intégré la classe ArrayObject , qui permet de traiter l'objet comme un tableau et peut être pratique lorsqu'il est utilisé comme conteneur pour l'enregistrement ou la collecte de la base de données.

Ce n'est peut-être pas une meilleure pratique en ce qui concerne les types stricts, mais cela permet de traiter l'objet comme un tableau et les deux instructions sont valides.

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

Cependant, vous devez garder à l'esprit le comportement de 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 accepte deux drapeaux ArrayObject::STD_PROP_LIST Par défaut et ArrayObject::ARRAY_AS_PROPS Comme alternative.

Cela changerait le comportement de lecture des valeurs mais ne prend pas en charge la définition de nouvelles propriétés de cette manière, voici un exemple:

$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)
 */

Pour rendre ce comportement plus cohérent, vous devez étendre cette classe et implémenter les méthodes magiques __get(), __set(), __isset() et __unset().

Une autre partie délicate est la sérialisation, la méthode par défaut serialize vous retournerait une variable sérialisée $storage Au lieu de l'objet lui-même, comme solution de contournement pour retourner une copie sérialisée d'instance, vous pouvez implémenter la sérialisation par défaut dans __toString, de cette façon, elle se comporte correctement.

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);
    }
}

Exemple d'utilisation

$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

Une façon de procéder, sans modifier la définition de classe d'origine, consiste à utiliser la réflexion. Cela vous permet d'examiner les propriétés de la classe lors de l'exécution.

Tiré du manuel: 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

Vous pouvez utiliser get_object_vars ($ yourObject) qui renverra un tableau associatif de tous les noms/valeurs de propriété accessibles à partir du contexte.

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

Si vous souhaitez accéder à des propriétés protégées ou privées, mon conseil serait d'étendre ArrayObject, qui implémente la méthode getArrayCopy ()

1
Aghanim