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

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.

Hirdetés

3 pénzügyi döntés, amit minden kisvállalkozónak érdemes átgondolnia az év végéig

PR Ahogy az év vége közeledik, itt az ideje, hogy egy pillanatra megálljunk és áttekintsük vállalkozásunk pénzügyi helyzetét. Ne hagyjuk, hogy az év utolsó hónapjai elússzanak a sürgető feladatok és elfeledett határidők között!

  • 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