2

PHP でクラスのパブリック データ メンバーを遅延ロードしたいと考えています。次のクラスがあるとします。

<?php
class Dummy
{
    public $name;
    public $age;
    public $status_indicator;
}
?>

とがプライベート データ メンバーである場合$name、getter メソッドを介して遅延ロードしますが、パブリックであるため、遅延ロードの方法がわかりません。これは可能ですか?$age$status_indicator

編集__get:誰かが、この問題を解決するのに役立つかもしれないメソッドがあるとコメントしましたが、私はそれを理解していませんでした.

4

9 に答える 9

2

__get最初のアクセス時に実際に動的にロードされるパブリック メンバーをシミュレートするために使用できます。オブジェクトの未定義のメンバーにアクセスしようとすると、PHP が呼び出さ__getれ、アクセスしようとしたメンバーの名前が渡されます。たとえば、クラスがメソッドを定義している場合、アクセス$x->my_variableは呼び出します。__get("my_variable")__get_

この例で$dummy->nameは、 はゲッター メソッドを間接的に呼び出します。このメソッドは、最初のアクセスgetNameで指定されたプライベート メンバーを初期化します。$_name

<?php
class Dummy
{
  private $_name;

  public function __get($var) {
    if ($var == 'name') {
      return $this->getName();
    } else if ($var == 'age') {
      // ...
    } else {
      throw "Undefined variable $var";
    }
  }

  public function getName() {
    if (is_null($this->_name)) {
      // Initialize and cache the value for $name
      $this->_name = expensive_function();
    }
    return $this->_name;
  }
}

$dummy = new Dummy();
echo $dummy->name;

のような他のアクセサーを同様に定義して呼び出すことができますgetAge

于 2012-05-17T17:51:49.960 に答える
1

最近のプロジェクトで使用したコードを次に示します。

class EnhancedObject {
    #Store cached properties here
    private $lazyProperties = array();

    #Allow $object->prop to alias to $object->getProperty().
    #Also, allow caching of $object->loadProp() to $object->prop
    public function __get($property) {
        $getter = "get".ucfirst($property);
        $loader = "load".ucfirst($property);

        if(method_exists($this, $getter)) {
            return $this->$getter();
        }
        elseif(method_exists($this, $loader)) {
            if(!isset($this->lazyProperties[$property])) {
                $this->lazyProperties[$property] = $this->$loader();
            }
            return $this->lazyProperties[$property];
        }
    }

    #Allow $object->prop = $value to alias to $object->setProperty($value).
    public function __set($property, $value) {
        $setter = "set".ucfirst($property);
        $loader = "load".ucfirst($property);

        if (method_exists($this, $setter)) {
            return $this->$setter($value);
        }
        elseif(method_exists($this, $loader)) {
            $this->lazyProperties[$property] = $value;
        }
        return $this;
    }
}

これは、魔法__getset一度いじるだけでよいことを意味し、メソッドに適切な名前を付けるだけで、ゲッター、セッター、および遅延初期化子として動作します。

使用法

class Dummy extends EnhancedObject {
    public $name;
    public $age;

    #Complex getters
    public function getSummary() {
        return "{$this->name}, aged {$this->age}";
    }

    #Cached getters for expensive operations
    public function loadStatusCount($id) {
        doExpensiveStuff();
        return 42;
    }
}

$d = new Dummy();
$d->name = "John Doe"
$d->age = 35

#getters
echo $d->summary; # echos "John Doe, aged 35"

#Lazy-initialized properties
echo $d->statusCount; # runs `doExpensiveStuff()`, echoing 42
echo $d->statusCount; # echos 42
echo $d->statusCount; # echos 42
于 2012-05-17T17:51:54.737 に答える
0

このような「lazy-loading-properties」を持つのは良いコーディングスタイルではないと思います。遅延読み込み値が必要な場合は、パブリックプロパティなしでget-methodを使用するだけです。

しかし、私はこの問題が好きです:)そして私はほとんどの解決策も好きですが、ここに私の一般的な解決策も追加したいと思います:

class Dummy
{
    private $_name;
    private $_age;
    private $_number_of_status;

    public function __get($name) {
        $var = "_".$name;
        $lazyLoadingMethod = "_load_".$name;
        if (!method_exists($this, $lazyLoadingMethod )) {
            trigger_error("Cannot access private property Dummy::$$name", E_USER_ERROR);
            return;
        }
        if (!isset($this->$var)) {
            $this->$var = $this->$lazyLoadingMethod();
        }
        return $this->$var;
    }

    public function __set($name, $value) {
        $var = "_".$name;
        $settingMethod = "_set_".$name;
        if (!method_exists($this, $settingMethod )) {
            trigger_error("Cannot access private property Dummy::$$name", E_USER_ERROR);
        } else {
            $this->$settingMethod($value);
        }
    }

    public function _load_age() {
        // lazy load here
        return 42;
    }

    public function _set_name($name) {
        echo "my new name is $name";
        $this->_name = $name;
    }
}

このクラスでは、プロパティの読み取り/書き込みメカニズムもあるため、「年齢」は読み取り可能ですが設定できません。「名前」は設定できますが、読み取りはできません。

$d = new Dummy();
echo $d->age; //=> 42
$d->name = "Jon"; //my new name is Jon
echo $d->name; //=> Fatal error: Cannot access private property Dummy::$name in ...
$d->age = 100; //=> Fatal error: Cannot access private property Dummy::$age in ...
于 2012-11-05T18:54:00.617 に答える
0

コメントを拡張するために、フィールドの配列と小さな魔法のメソッドmagicを使用して、読み込み操作を処理できます。

<?php
class Dummy
{
    protected $fields = array(
        'name' => null, 
        'age' => null,
        'etc' => null
    );

    public function __get ($key) {
        if (array_key_exists($key, $this->fields)) {
            if ($this->fields[$key] === null) {
                // do some kind of loading operation to set $value
                $this->fields[$key] = $value;
            }
            return $this->fields[$key];
        }
        return null;
    }
}
?>
于 2012-05-17T17:52:03.023 に答える
0

それらを削除して、魔法のメソッド__get()を使用するだけです。

何かのようなもの:

class Dummy 
{ 
   public function __get($var_name) 
   {
      switch ($var_name) 
      {
        case 'name':
            // lazy load here
            return $val;
            break;

        case 'age':
            // lazy load here
            return $val;
            break;

        case 'number_of_status':
            // lazy load here
            return $val;
            break;
      }
   }
}
于 2012-05-17T17:53:17.773 に答える
0

http://us.php.net/manual/en/language.oop5.overloading.phpを使用__get()

public function __get($name)
{
    if ($name == "age" and $this->age == null)) {
        // lazy load $this->age
        return $this->age;
    }

    return $this->$name;
} 
于 2012-05-17T17:54:20.680 に答える
0

まず第一に、これがまさに getter/setter メソッドを使用すべき理由です。これにより、データを取得するプロセスにロジックを追加できます。

__get() を使用して public-member のようなアクセスを実現する方法の例を次に示します。

class Dummy {
    private $lazyMembers = array(
        'name' => 'NameOfTheClass'
    );

    public function __get($key) {
        if (isset($this->lazyMembers[$key])) {
            $obj = new $this->lazyMembers[$key]();
            $this->$key = $obj;
            return $obj;
        }

        // ..throw exception or whatever
    }
}
于 2012-05-17T17:54:27.707 に答える
0

あなたの質問を理解したように、パブリック変数をオーバーロードできるかどうかを知りたがっていました。

__get($name) マジック メソッドについては既にご存知でしたよね? または、 getName()getAge()、および *getStatus_Indicator()*について話していたのかもしれません。

いずれにせよ、パブリック プロパティとして魔法のメソッドを利用することはできません。

概念実証のためにそれらをリストします。

例 1

<?php

class Dummy {
    public $name;
    public $age;
    public $status_indicator;

    public function __construct() {
        foreach($this AS $name => $value) {
            $this->$name = new LazyLoader($this, $name);
        }
    }
}



class LazyLoader {
    protected $object;
    protected $name;

    public function __construct($object, $name) {
        $this->object = $object;
        $this->name = $name;
    }
    public function load() {
        /* find the necessary $data somehow */
        $name = $this->name;
        $this->object->$name = $data;
        /*
           after the first call to LazyLoader::load
           the property no longer points to a LazyLoad
           object, but to the intended value
        */
    }
    public function __toString() {
        return($this->load());
    }
}

?>

例 2

<?php

class LazyCollectionWrapper extends ArrayObject {
    public function __construct($objects = array()) {
        parent::__construct($objects);
    }

    public function offsetGet($offset) {
        $this->collection[$offset]->lazyLoad();
        return($this->collection[$offset]);
    }
}

class Dummy {
    public $name;
    public $age;
    public $status_indicator;

    public function lazyLoad() {
        /* load stuff here */
    }
}

?>

例 3

<?php

class Dummy {
    public function __get() {
        /*
          load stuff here because undefined
          properties are caught by __get too
        */
    }
}

?>

例 3 は構造についてはあまり情報がありませんが、メモリに関する限り、これが最良の方法です。私たちは遅延ロードの話をしていました...つまり、役に立たないものをメモリにロードしませんよね?

私がこれを言うのは:

class x { protected $var1 = array(); protected $var2 = 0.0; }

よりも多くのメモリを消費します

class x { protected $var1; protected $var2; }

そしてそれ以上に

class x { }

何百万ものオブジェクトを構築し、ピーク時のメモリ使用量を比較してテストしました。

于 2012-05-17T18:21:12.027 に答える
0

OP と同じ例を使用すると、保護/プライベートのようにパブリック メンバーを遅延ロードすることはできません。OPが言及したようないくつかの回避策があり、ゲッターメソッドを使用して遅延読み込みロジックを実行します。メンバーへの直接アクセスは決して呼び出されない__get()ため、同じクラスのパブリック メンバーでは機能しません。__get()それらを配列に隠すと、__getまたはgetterメソッドが機能しますが、それは基本的に、明らかに回避しようとしているパブリックな可視性からそれらを削除することと同じです。

于 2012-05-17T17:57:47.590 に答える