PHP и его встроенные OOP мелочи

    Почитал еще в «том году» статью о PHP и OOP под названием PHP и магия ООП, тамошний Девид Блейн описал в краце что есть таки OOP в PHP… Да, автор показал как «надо» использовать правильно функции, но в наше время, когда языки стали не обьектно ориентированными а обьектными, уже не удивишь простой поддержкой OOP. Многие гнобят PHP тем что он корявый, медленный… Ну подумаешь, медленный, подумаешь параметры функций принимаются в разном порядке =) его можно ускорить акселераторами, функции можно скрыть в классах… Самая мошь OOP в PHP — встроенные интерфейсы… Вот о 2-х из них (на примере массива) я и расскажу…

    Интерфейсы — вершина айсберга под названием SPL, про эту штуку я узнал здесь же, от автора загадки: сделайте чтоб код возвращал тру — if( $q[0] === 1 && $q[0] === 2 ) (точно задание не помню, я его перефразировал, так интересней)

    Для начала код, разьяснения потом:
    <?php

    class MyArray implements ArrayAccess, Iterator
    {
      private $_data;
      private $_innerCounter = -1;

      public function __construct( $data = null )
      {
        $this->_data = $data;
        $this->_innerCounter = count( $data );
      }

      public function get( $offset )
      {
        return $this[$offset];
      }
      
      // start implementing ArrayAccess interface

      public function offsetExists( $offset )
      {
        return isset( $this->_data[$offset] );
      }

      public function offsetGet( $offset )
      {
        if( is_array( $this->_data[$offset] ) )
        {
          $this->_data[$offset] = new MyArray( $this->_data[$offset] );
    return $this->_data[$offset];
        }

        return $this->_data[$offset];
      }

      public function offsetSet( $offset, $value )
      {
        $this->_data[$offset] = $value;
        return $this;
      }

      public function offsetUnset( $offset )
      {
        unset( $this->_data[$offset] );
        return $this;
      }

      // end implementing ArrayAccess interface  
      
      // start implementing Iterator interface  
      
      public function current()
      {
        if( is_array( current( $this->_data ) ) )
        {
          return new MyArray( current( $this->_data ) );
        }

        //return $this->_data[$offset];
        return current( $this->_data );
      }

      public function next()
      {
        $this->_innerCounter++;
        return next( $this->_data );
      }

      public function key()
      {
        return key( $this->_data );
      }

      public function valid()
      {
        return $this->_innerCounter < count( $this->_data );
      }

      public function rewind()
      {
        $this->_innerCounter = 0;
        //return reset( $this->_data );
        return;
      }
      
      // end implementing Iterator interface 
    }

    $array = new MyArray( array(1, 2, 'qwe' => 'asd', 6, 'obj' => array(1,2)) );

    foreach( $array as $arrayKey => $arrayValue )
    {
      if( is_a( $arrayValue, 'MyArray' ) )
      {
        echo "it`s an MyArray Object with key '{$arrayKey}' -> ";
        var_dump( $arrayValue );
      }
      else
      {
        var_dump( 'key = ' . $arrayKey . ' value = ' . $arrayValue );
      }
      echo '<br>';
    }

    var_dump( $array['qwe'] );
    echo '<br>';
    var_dump( $array->get('qwe') );
    echo '<br>';
    var_dump( $array['obj'] );
    echo '<br>';
    var_dump( $array->get('obj')->get(1) );

    ?>

    // результат:
    // string(17) "key = 0 value = 1"
    // string(17) "key = 1 value = 2"
    // string(21) "key = qwe value = asd"
    // string(17) "key = 2 value = 6"
    // it`s an MyArray Object with key 'obj' -> object(MyArray)#3 (2) { ["_data:private"]=> array(2) { [0]=> int(1) [1]=> int(2) } ["_innerCounter:private"]=> int(2) }
    // string(3) "asd"
    // string(3) "asd"
    // object(MyArray)#2 (2) { ["_data:private"]=> array(2) { [0]=> int(1) [1]=> int(2) } ["_innerCounter:private"]=> int(2) }
    // int(2)

    * This source code was highlighted with Source Code Highlighter.


    Так вот… Есть класс MyArray которые применяет 2 интерфейса: ArrayAccess и Iterator… Что же они делают? Начнем попорядку.

    ArrayAccess: интерфейс дающий возможность доступа к Вашему обьекту как к массиву (доступ через квадрытные скобочки см коментарии в коде ) пример: $array[1] или $array['qwe']
    Незаменимый помощник в создании массивов!
    Структура:
    ArrayAccess {
    /* Methods */
    // проверка на существование значения по ключу ( $offset - ключ массива )
    abstract public boolean ArrayAccess::offsetExists ( string $offset )
    // возврат по ключу ... аля $array['offset']
    abstract public mixed ArrayAccess::offsetGet ( string $offset )
    // задание значения по ключу ... аля $array['offset'] = 'value'
    abstract public void ArrayAccess::offsetSet ( string $offset , string $value )
    // ансет ... ну тут думаю все понятно :) unset $array['offset']
    abstract public void ArrayAccess::offsetUnset ( string $offset )
    }


    Iterator: Интерфейс дающий возможность перебора через foreach/for (возможность перебора есть у всех обьектов, а тут есть контроль ;)) членов обьекта… Естественно каких вы пожелаете (поумолчанию идут только паблик свойства)
    Структура:
    /* Methods */
    // аналог current( array(1,2,3) )
    abstract public mixed Iterator::current ( void )
    // аналог key( array(1,2,3) )
    abstract public scalar Iterator::key ( void )
    // аналог next( array(1,2,3) )
    abstract public void Iterator::next ( void )
    // аналог rewind( array(1,2,3) )
    abstract public void Iterator::rewind ( void )
    // аналог isset( $array[1]) - вроде =)
    abstract public boolean Iterator::valid ( void )


    Итак, что же мы получили? Мы получили базовый обьект с одним «ручным» методом get, который возвращает элемент массива по ключу. Спрашивается зачем он нам если есть ArrayAccess? Ответ очевиден (тут я задумался и проверил возможность такого действия: $array['qwe']['asd']… Так работает =) я счастлив!): надо ведь хоть один метод реализовать, хоть для виду?!?!

    В общем это уже конец, дальше любые методы реализовать не сложно, базовый функционал есть, полноценный массив есть, поддержка циклов есть, контроль над выдаваемыми данными есть… Добавляйте, изменяйте, отдаю на Ваше распоряжение…

    зы: PHP — удивительный язык, со всей его немошью, на нем ножно сделать практически все, со всей его неструктурированностью, его можно полностью структурировать под себя, со всей его медлительностью, его можно разогнать до скорости компилируемых языков… я люблю этот язык какие бы у него ни были недостатки, чего и вам, PHP-щики желаю — не вешайте нос!
    зыы: если ты здесь не был — ты лох! =) уж извените за выражения, но никакой мануал не восполнит этого сайта :)

    UPD: по просьбам трудящихся пояснение: этот обьект не будет себя вести как полноценный массив в силу того что массивы простые типы данных, а обьекты ссылочные, и в некоторых ситуациях это свойство себя проявляет
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 23

      0
      Код возможно не будет изначально работать, во время написания статьи я его несколько раз менял для удобства
        +1
        «возможность перебора через foreach/for»
        точнее через foreeach, for — не для этого

        «возможность перебора есть у всех обьектов»
        всмысле?

        Что у вас sql в тегах делает?
          +1
          SPL :)
            +1
            Yarrr… извиняюсь
            0
            никто не заставляет использовать этот обьект как ассоциативный массив, можно и как обычный, все в Ваших руках
              0
              > возможность перебора есть у всех обьектов
              с помошью foreach можно перебирать public свойства всех обьектов
              0
              Про разгон до скорости компилируемых языков, пожалуйста, поподробней :-)
                –1
                оптимизаторов куча, кэширующих средств тоже немеряно, как только код и данные попадают в память то о скорости уже не стоит задумываться… одно но — память =)
                  0
                  club.shelek.ru/viewart.php?id=300
                  я конечно утрировал насчет скорости компилируемых, но разогнать можно очень сильно, до 10 раз :)
                  +1
                  Не поддерживается несколько обходов одновременно:

                  $array = new MyArray(array(7, 8, 9));
                  foreach($array as $firstKey => $firstValue)
                  {
                    foreach($array as $secondKey => $secondValue)
                    {
                      print "$firstValue, $secondValue\n";
                    }
                  }

                  Выводит:
                  7, 7
                  7, 8
                  7, 9


                  Не работает присваивание вложенным массивам:

                  $array = new MyArray(array(4, 5, 6, array(7, 8, 9)));
                  echo $array[3][0], "\n";
                  $array[3][0] = 100;
                  echo $array[3][0], "\n";

                  Выводит 7 и 7.
                    –1
                    я не ставил цель сделать замену массива обьектом, я лишь показал работу двух интерфейсов и базовый функционал который реализуется с помошью них, дальше лишь полет Вашей фантазии
                      –1
                      вообще, если Вы ожидали такой результат:
                      7, 7
                      7, 8
                      7, 9
                      8, 7
                      8, 8
                      8, 9
                      9, 7
                      9, 8
                      9, 9

                      то смею Вас огорчить: обьект — ссылочный тип, в отличии от массива
                        +1
                        Я (как любой программист) ожидаю, что ваш MyArray будет работать подобно встроенному массиву (хотя бы потому что он так называется и реализует соответствующие интерфейсы). И смотрю не на мелкие особенности, а на самые базовые варианты использования.

                        Тем более что ваша статья для новичков («продвинутые» уже видели такие фичи и в php, и в других языках). Возьмут, скопируют и будут думать, почему не работает.
                          0
                          на сколько мне известно, любая книга по PHP в самом начале обьясняет что массивы — простой тип данных, обьект — ссылочный, при мизерном стаже работы сразу визуально заметно что массив копируется а обьект ссылается… так что я скорее всего не соглашусь с Вами, не все будут ожидать такого результата

                          зы: ни разу не приходилось перебирать один массив в двух циклах если честно… на крайняк, если это Вам уж очень надо, можно реализовать клонирование обьекта
                            +2
                            Если я второй пример перепишу так, то думаю, что станет понятнее:
                            doit(new MyArray(array(4, 5, 6, array(7, 8, 9))));
                            doit(array(4, 5, 6, array(7, 8, 9)));
                            
                            function doit($data)
                            {
                              echo $data[3][0], "\n";
                              $data[3][0] = 100;
                              echo $data[3][0], "\n";
                            }


                            Мы получили класс, экземпляры которого ведут себя как массивы. Код (функция doit) может даже не знать, с чем он работает, с массивом или с MyArray. Массив нормально реагирует на изменения, а MyArray — нет, причём «тихо», не вызывая ошибок. И присвоенные данные пропадают вникуда.

                            Два вложенных обхода по одному и тому же массиву вы, непосредственно может и не писали. Но вполне вероятна такая ситуация, что внутри foreach вы вызываете некоторую функцию над этим же массивом, и эта функция тоже обходит массив. Если это будет настоящий массив — всё хорошо. Если MyArray — будете долго отлаживать.
                              0
                              как я писал выше, я не ставил задачу сделать аналог массива, вот Вы прям прицепились к тому что обьект должен работать обязательно как массив и все, это физически не возможно условиями интерпритатора и среды выполнения, так пожелали разработчики
                                0
                                см upd. я надеюсь это обьясняет теперь Ваши удивления?
                                +1
                                Просто надо в статье сделать дисклаймер, что пример не является полной эмуляцией массива, хорошо бы с объяснение причин от «сделан только для демонстрации» до " это сделать невозможно" ;)
                                  0
                                  еще раз повторю: цель топика не обьект для замены массива а обзор 2-х интерфейсов дающих обьекту дополнительные свойства которые нельзя обеспечить стандартными средствами OOP
                                    0
                                    см upd.
                              0
                              изменил метод offsetGet() теперь работает присваивание влоденным массивам
                              0
                              Давно мучает вопрос:
                              Вот есть фильтр на SPL для вывода файлов определенного типа из директории
                              <?php 
                              class F_FileIterator extends FilterIterator { 
                                  private $ext; 
                                  private $it; 

                                  function __construct(DirectoryIterator $it, $ext) { 
                                      parent::__construct($it); 
                                      $this->it=$it; 
                                      $this->ext=$ext; 
                                  } 

                                  function accept() { 
                                      $ext=pathinfo($this->current(),PATHINFO_EXTENSION); 
                                      return ($ext==$this->ext) ? true : false; 
                                  } 


                              $d=new F_FileIterator(new DirectoryIterator('.'), 'php'); 

                              foreach ($d as $v) { 
                                  echo "$v<br />\n"; 

                              ?>



                              а как сделать компаратор на ооп? Тоесть чтобы они выводились в нужном мне порядке, а не от балды.
                                0
                                вообще, они следуют не от балды а попорядку в файловой системе, как в FilterIterator сделать чтоб он их сортировал, если честно, не знаю, но напрашивается обвертка для всего этого (допустим F_Files) с методом getSorted( )

                              Only users with full accounts can post comments. Log in, please.