PHP iteráció

PHP-val képesek vagyunk megoldani azt, hogy egy-egy objektumunk iterálható legyen foreach-csel, avagy kompletten tömbként működjön. Most vegyük az egyszerűbbet, és nézzük meg, hogyan kell iterálható osztályt írnunk:

http://php.net/manual/en/class.iterator.php

Hirdetés

Készen is vagyunk, örülünk.

De.

A PHP SPL könyvtárában akad egy olyan varázslat, amit IteratorAggregate-nek hívunk. Ez szintén segít megoldani a problémánkat, csak sokkal egyszerűbben. Itt csak egy metódust kell implementálnunk, ami visszaad egy ArrayIterator-t. Ugye, hogy sokkal szebb?!

De.

Van egy másik hatalmas előnye a második verziónak: gyorsabb.
Készítettem egy házitesztet, aminek az eredménye ez lett:
IteratorAggregate: 12,5+ másodperc
Iterator: Fatal error: Maximum execution time of 200 seconds exceeded in D:\xampp\htdocs\ref\index.php on line 26

Nem tudom, hogy vagytok vele, de én frissítem a kódjaimat.

  • Peter Kiss

    őstag

    válasz Sk8erPeter #9 üzenetére

    Melóban vagyok, a kód nincs nálam.

    ---

    Az plusz kód sem árul el semmit sem arról, hogyan fetcheli az obejktumokat. Egyébként szerintem nagyon f.s érdekes ez a kód. :DDD

  • Sk8erPeter

    nagyúr

    válasz Peter Kiss #8 üzenetére

    "mi volt a tesztkörnyezet"
    Itt eléggé pontatlan volt, amit írtam, mert nem csak a konfiguráció érdekelt volna, hanem a konkrét kód.

    A Kohanás kódra:
    igazából abbahagytam a bemásolást, és nem raktam be azt, amit még kellett volna: a _load_result() függvényt:

    ..............
    /**
    * Loads a database result, either as a new record for this model, or as
    * an iterator for multiple rows.
    *
    * @chainable
    * @param bool $multiple Return an iterator or load a single row
    * @return ORM|Database_Result
    */
    protected function _load_result($multiple = FALSE)
    {
    $this->_db_builder->from(array($this->_table_name, $this->_object_name));

    if ($multiple === FALSE)
    {
    // Only fetch 1 record
    $this->_db_builder->limit(1);
    }

    // Select all columns by default
    $this->_db_builder->select($this->_object_name.'.*');

    if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting))
    {
    foreach ($this->_sorting as $column => $direction)
    {
    if (strpos($column, '.') === FALSE)
    {
    // Sorting column for use in JOINs
    $column = $this->_object_name.'.'.$column;
    }

    $this->_db_builder->order_by($column, $direction);
    }
    }

    if ($multiple === TRUE)
    {
    // Return database iterator casting to this object type
    $result = $this->_db_builder->as_object(get_class($this))->execute($this->_db);

    $this->reset();

    return $result;
    }
    else
    {
    // Load the result as an associative array
    $result = $this->_db_builder->as_assoc()->execute($this->_db);

    $this->reset();

    if ($result->count() === 1)
    {
    // Load object values
    $this->_load_values($result->current());
    }
    else
    {
    // Clear the object, nothing was found
    $this->clear();
    }

    return $this;
    }
    }
    .....

  • Peter Kiss

    őstag

    válasz Sk8erPeter #7 üzenetére

    Ebből semmi sem látszik, mert ez csak query builder + végrehajtás, de az eredmény már nincs benne. modder azt írta, hogy folyamatosan fetchel, ebben ez nincs benne, és én ezt kétlem. Azt, hogy iterálható az adathalmaz, az nem vita tárgya, az, ha működik.

    ---

    Nem, addig nem jelenik meg.

    Core i5 @3GHz 1 magon 128 vagy 256 MB memóriát adva a PHP-nak.

  • Sk8erPeter

    nagyúr

    válasz Peter Kiss #6 üzenetére

    Attól még, mert írtál ORM-et (ami önmagában azért nem varázslat, még ha szopó feladat is lehet), egy másik framework miért ne működhetne másképp, mint ahogy Te megírtad?
    modder ismeri a Kohanát, dolgozott vele, arra alapozva mondja.
    Azt mondjuk nem értem, Te mire alapozva cáfoltad, hogy nem úgy működik, ahogy ő írta, ha ezek szerint meg sem nézted. :)

    Én nem ismerem a Kohanát, ezért vettem rá a fáradságot, hogy letöltsem a keretrendszert és megnézzem, mi lehet az igazság, itt van kimazsolázva az érintett kódrészlet:

    class Kohana_ORM extends Model implements serializable {

    //...................................................

    /**
    * Database query builder
    * @var Database_Query_Builder_Where
    */
    protected $_db_builder;

    //...................................................

    /**
    * Initializes the Database Builder to given query type
    *
    * @param integer $type Type of Database query
    * @return ORM
    */
    protected function _build($type)
    {
    // Construct new builder object based on query type
    switch ($type)
    {
    case Database::SELECT:
    $this->_db_builder = DB::select();
    break;
    case Database::UPDATE:
    $this->_db_builder = DB::update(array($this->_table_name, $this->_object_name));
    break;
    case Database::DELETE:
    $this->_db_builder = DB::delete(array($this->_table_name, $this->_object_name));
    }

    // Process pending database method calls
    foreach ($this->_db_pending as $method)
    {
    $name = $method['name'];
    $args = $method['args'];

    $this->_db_applied[$name] = $name;

    call_user_func_array(array($this->_db_builder, $name), $args);
    }

    return $this;
    }


    //...................................................

    /**
    * Finds multiple database rows and returns an iterator of the rows found.
    *
    * @return Database_Result
    */
    public function find_all()
    {
    if ($this->_loaded)
    throw new Kohana_Exception('Method find_all() cannot be called on loaded objects');

    if ( ! empty($this->_load_with))
    {
    foreach ($this->_load_with as $alias)
    {
    // Bind auto relationships
    $this->with($alias);
    }
    }

    $this->_build(Database::SELECT);

    return $this->_load_result(TRUE);
    }

    //...................................................

    } // End ORM

    Na most a többi érintett kódrészletet már nem másolgatom be, mert akkor kicsit hosszú lenne a hozzászólásom, de ami ebből is látható (meg a többi kódrészletből), az alátámasztani látszik azt, amit modder írt.

    ===

    (#5) : Ezek szerint addig nem jelenik meg a buborék, amíg meg nem jelenik az első hsz.? Hmm.
    A példára rátérve igazából arra lettem volna kíváncsi, hogyan tesztelted, ami miatt a sima Iterator interface implementációjával történő tesztelés elszállt futási idő túllépésével. Meglepő, ha ekkora a különbség, ilyen esetben meg azért nem árt tudni, mi volt a tesztkörnyezet, amiben ez teljesült.

  • Peter Kiss

    őstag

    válasz modder #3 üzenetére

    Nem hiszem, hogy ilyen varázslat lenne a Kohana-ban. Túlságosan bonyolítaná a dolgokat, ha nem fetchelnék objektummá azonnal az elemeket, mondom ezt úgy, hogy építettem ORM-et. Az előző hozzászólásomban írtam, hogyan pakoltam bele az enyémbe, annál fogva:

    foreach ($dataContext->Members() as $member) {
    /* ... */
    }

    Gyakorlatban használok ilyet mindenféle container (EntitySet, ObjectGroup, Enumerable, stb) bejárására, de pl. a Config osztályom is tudja, ott a gyerek elemeket lehet bejárni (XML-ből táplálkozik). De pl. a saját session-kezelésből adódik egy ACSession osztályom, aminél a $_SESSION-t tudom bejáratni, ha ahhoz van kedvem.

  • Peter Kiss

    őstag

    válasz Sk8erPeter #2 üzenetére

    Megoszthatom a tesztet, de nem akarok nevetség tárgya lenni, annyira faék. :DDD

    Gyakorlati használata rém egyszerű (egyszerű példában persze :DDD ):

    class IT implements IteratorAggregate {

    protected $_a = array();

    public function getIterator() {
    return new ArrayIterator($this->_a);
    }

    }

    TableEntity-mben:
    abstract class TableEntity extends \System\ObjectBase implements \IteratorAggregate, \System\Collections\IEnumerable {

    /* ... */

    public function getIterator() {
    return new \ArrayIterator($this->ToArray());
    }

    }

    ---

    Amint kiküldöm az első hozzászólást, kapcsolok is rá a borítékra. ;)

  • Sk8erPeter

    nagyúr

    válasz modder #3 üzenetére

    Ezt jó tudni.
    Egyébként a korábbi hsz.-emben arra céloztam, hogy kíváncsi lennék, hogyan nézett ki Athlon64+-nál az összehasonlítás (maga a kód), de rájöttem, hogy a hivatalos oldalon mutatott IteratorAggregate-re mutatott példa annyira egyszerű, hogy túl szép, hogy igaz legyen. :D

    ..............
    public function getIterator() {
    return new ArrayIterator($this);
    }
    ..............

    Hát ez ilyenformán nem egy nehéz valami.
    Gondolom ilyenre lefuttatott egy buzinagy ciklust egy kis mérőkével, meg a másikra is, és kész.

    (#1) Athlon64+ :
    "[értesítő]"
    így nem egyszerűbb? >>
    :P

  • modder

    aktív tag

    válasz Sk8erPeter #2 üzenetére

    gyakorlati hasznát én ennek pl. a Kohana ORM moduljában láttam, ahol egy lekérdezés pl.:
    ORM::factory('Entitas')->find_all()
    egy iterálható objektumot ad vissza entitásokról, és végig lehet rajta menni foreach-csel. A háttérben pedig a mysql result set-ből dolgozik. Az értelme valami olyasmi, hogy nem alakítja az összes adatbázis sort objektummá, csak akkor, amikor konkrétan el akarod érni.

  • Sk8erPeter

    nagyúr

    Ezt is jó tudni, köszönjük!
    Én személy szerint kíváncsi lennék a gyakorlati használatára is - a saját tesztedet esetleg nem publikálod?
    Ugyanezt akkor lehetne alakítgatni egyéni tesztek írására is.

    Btw. örülök, hogy valaki itt PH!-n haladó módon is foglalkozik a PHP-val, és végre nem csak olyan színvonalú írásokat látni, hogy "hogyan validáljunk és dolgozzunk fel egy formot szerveroldalon PHP-val". Így tovább!

Még van hozzászólás! Tovább