Прекрати злоупотреблять массивами в PHP

Original author: Gary Willoughby
  • Translation
  • Tutorial
php arrays everywhere

Меня давно мучает мысль об одной проблеме — тотально злоупотребление массивами в PHP. Возможно корень проблемы в процедурном наследии PHP или в том, что PHP привлекает большое количество неопытных разработчиков, не знаю. Но дело в том, что очень многие используют массивы там, где должны использоваться объекты!

Взглянем на типичный пример ужасающего, на мой взгляд кода, злоупотребляющего массивами. Для примера, предположим, мы извлекаем данные из БД и работаем с ними посредством массива. Вот такие примеры я вижу ежедневно:

$ponds = array(
	array(
		"name" => "Breakspear",
		"size" => "large",
		"pegs" => "23",
		"amenities" => array(
			"toilets" => false,
			"shop"    => true,
		),
		"fishBreeds" => array(
			array(
				"name"    => "Bream",
				"stocked" => "2013-10-04 12:16:47",
				"number" => "100",
				"record"  => "5.4lbs",
			),
			array(
				"name"    => "Perch",
				"stocked" => "2012-02-02 05:23:32",
				"number" => "50",
				"record"  => "1.2lbs",
			),
			array(
				"name"    => "Common Carp",
				"stocked" => "2011-01-23 14:42:59",
				"number" => "10",
				"record"  => "15.4lbs",
			),
		),
	),
);

Мы видим огромный многомерный массив, хранящий информацию о конкретном рыболовном пруду. Здесь описан только один пруд, но представьте, что было бы, будь там описана сотня прудов? Что в итоге? Мы имеем набор данных, хранящийся в массиве, но не имеющий никакого связанного поведения. Когда нам нужно будет работать с этими данными, нам придется создавать сложный код, полный вложенных циклов. Например, как я могу получить общее количество рыб в пруду? Мне придется пройтись по всему массиву и сложить все количество рыб. Для малоопытного разработчика это не показалось бы чем-то плохим, так бы он и сделал, но мне больше по душе пришелся бы такой подход:

$ponds->getNamed("Breakspear")->getTotalStocked();

Намного меньше кода для получения того же результата. Конечно, полного перебора данных не избежать, но функциональность красиво инкапсулирована. Собственно, этот пример и раскрывает основную проблему — не стоит избегать преимуществ ООП. Примерно так должен выглядеть код из первого примера:

$ponds = new PondCollection();
 
$pond = new Pond("Breakspear");
$pond->addStockData(new StockData("Bream", 100, "2013-10-04 12:16:47"));
$pond->addStockData(new StockData("Perch", 50, "2012-02-02 05:23:32"));
$pond->addStockData(new StockData("Common Carp", 10, "2011-01-23 14:42:59"));
$pond->addAmenity(new ShopAmenity());
 
$ponds->add($pond);

Излишняя сложность


Использование огромных массивов означают не только невозможность добавления связанного поведения, но и обязывает создавать сложные механизмы доступа к данным. Часто это реализовывается как многофункциональные классы со здоровыми методами, использующими вложенные циклы. Вроде этого
foreach ($ponds as $pond)
{
    foreach ($pond["fishBreeds"] as $breed)
    {
        $stockedFish += $breed["number"];
    }
}

Писать и поддерживать такой код сложно, особенно, если происходит какое-то изменение в структуре данных. Кроме того реализация методов не имеет ничего общего с данными, которые использует.

Используя классы для инкапсуляции может показаться, что кода в итоге приходится писать больше, но я гарантирую по своему опыту, что это обернется меньшими расходами в дальнейшем. Просто подумайте, не придется каждый раз писать новый цикл для перебора значений. Кода будет меньше, он будет чище, вы будете видеть четкие границы того, что каждый класс делает.

Ты делаешь это неверно


Единственные причины, по которым разработчики продолжают писать подобный код — это или неопытность или потерянность в процедурном прошлом. ООП было изобретено как раз для подобного рода вещей, особенно для связывания поведения с состоянием. Поверите или нет, но я видел код очень популярной компании, работающей на рынке электронной коммерции использующий этот антипаттерн для таких важных частей как корзина и заказы. Есть места в коде, которые разработчики просто запрещают трогать, (несмотря на наличие багов) потому как код очень сложен и хрупок, что изменения просто невозможны. Сумасшествие.

Работа с наборами данных


Выбросьте из головы, что вам нужно использовать массивы для работы с набором данных. «Набор данных» (или коллекция) не означает использование массивов! Неопытные разработчики поступают так, потому что не слышали про итераторы. Их использование позволяет вам работать с коллекциями, реализуя конкретное поведение. На первый взгляд сложно, но на деле все просто. Вот класс, реализующий итератор

class PondsCollection implements IteratorAggregate
{
    private $collection = array();
 
    public function getIterator()
    {
        return new ArrayIterator($this->collection);
    }
 
    public function add($pond)
    {
        $this->collection[] = $pond;
    }
}

Вот и все. Реализация класса, который создает итерируемую коллекцию данных в сочетании с поведением. В своем сердце класс содержит массив, но который теперь оформлен с соответствующими методами для работы с данными. Реализация интерфейса IteratorAggregate делает объект класса доступным для работы с циклом.

$ponds = new PondsCollection();
$ponds->add(...);
 
foreach ($ponds as $pond)
{
    ...
}

Теперь нет необходимости в каждом месте, где нам необходимо получить данные из набора реализовывать связанное поведения, оно будет связано с коллекцией данных и доступно из объекта, а не разбросано по всему коду.

Подробнее в документации:

Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 131

    0
    Меня всегда очень бесило обилие массивов когда брал проекты на битрикс. Особенно если нужно было сделать доработки после реализации проекта джуном самоучкой. $arResult это просто trash, кто работал с битрикс меня поймет.
      +3
      Судя по тому, что ключи массивов написаны капсом — его писали блондинки.
      • UFO just landed and posted this here
          0
          Главная проблема битрикса конечно не в массивах, а в огромном техническом долге, который только усугубляется.
          А по поводу массивов — работа через объекты позволяет использовать автодополнение и как минимум избавляет от ошибок из-за опечаток. Ну тут уже встаёт вопрос падения производительности.
          • UFO just landed and posted this here
            • UFO just landed and posted this here
      • UFO just landed and posted this here
          +4
          Боюсь заминусуют но тут:
          new StockData("Perch", 50, "2012-02-02 05:23:32")

          можно и статическим методом обойтись, зачем каждый раз инстацировать объект, для выполнения одного вычисления?
            –4
            Не говори так!
              0
              Статические методы не ООП подход?
                0
                Я у автора спрашиваю, просто.
                  0
                  статические методы это обычные функции привязанные к контексту каких-то объектов, они обозначают что мол "я функция которая позволяет вам что-то сделать с объектами этого типа".
                  Статические методы — это нормально, плохо и не ОО подход — статические методы, которые оперируют каким-то своим состоянием. stateful-статика — это плохо, stateless статика — это нормально. Класс Utils и подобные — плохо, поскольку тогда мы просто пишем функции не привязанные ни к какому контексту, а стало быть лучше просто функции и писать. Благо с php 5.6/7 использование функций — одно удовольствие:
                  <?php
                  
                  use function \myCoolApp\array\{map, reduce, last};
                  
                  function makeSomethingUseful(array $collection) {
                      return reduce(
                           map($collection, function($item) {
                                   // do something
                                   return $item;
                           }),
                           function ($result, $item) {
                               return $result + $item;
                           }
                      );
                  }
                    0
                    статические методы это обычные функции привязанные к контексту каких-то объектов, они обозначают что мол «я функция которая позволяет вам что-то сделать с объектами этого типа».

                    Вы сейчас описали обычные функции, разве нет?
                    Единственная проблема, в том что язык не всегда помогает дополнять функциями существующие классы, поэтому иногда возникают "статические" функции.
                      +1
                      Я больше к тому что разницы между "функцией" и "статическим методом" с технической точки зрения нет. Разница лишь в семантике и доступе к внутреннему состоянию объекта. То есть по сути если функция "должна" знать о реализации объекта, скажем для удобства, то ее можно сделать статическим методом.

                      Например есть у нас ValueObject который содержит какое-то состояние, и мы хотим по нему отсортировать коллекцию. Причем делать геттер для конкретного поля нельзя. В итоге у нас появляется статический метод compare, который действует как обычная функция, но имеет доступ к внутреннему состоянию объекта своего типа. Пример таких извращений.
                        0
                        Простите, вот тут я ввел вас в заблуждения. Я согласен с вашими утверждениями, но это все еще не ООП подход, так как "функции" и "статический метод" нарушают принципы ООП.
                        • UFO just landed and posted this here
                            0
                            Вы сами поняли что написали?

                            Сингелтон — это вырожденный случай registry, который гарантирует нам то, что в рамках этого процесса будет порожден один единственный объект. Это нужно обычно только в оооочень редких случаях когда у нас есть физические ограничения. Например у нас может быть объект, который обеспечивает работу с каким-то внешним устройством через PCI или USB и вам смертельно важно сохранить последовательность операций иначе внешний девайс сломается например.

                            В WEB и конкретно в PHP сингелтоны… ну по сути вообще не нужны.

                            Так вот, вернемся к вашему… случаю. У "сингелтона" по сути нет никаких статических методов кроме как "create". По сути этот stateful статический метод и есть имплементация паттерна сингелтон. Далее все остальные методы — обычные методы.

                            И все зло сингелтона — это stateful статика. Больше никакого зла. Ну и я уже описал случаи когда он нам нужен, и для таких случаев пойти на такое вот "нарушение" вводя "почти глобальное" состояние можно. В PHP таких задач… меньше десятитысячной доли процента.
                            • UFO just landed and posted this here
                                0
                                Почитайте мануал http://php.net/manual/ru/language.oop5.static.php и особенно строку
                                Так как статические методы вызываются без создания экземпляра класса, то псевдо-переменная $this не доступна внутри метода, объявленного статическим.

                                Статические методы не инстацируют объект, так что ваша теория
                                Так вот, что я хотел сказать-то. Класс и его статические методы можно представить себе как синглетон, который в общем случае может быть оправдан, а может быть натягиванем совы на глобус

                                Немного притянута…
                                простейший пример синглтона
                                class psevdoDb {
                                
                                    private $_connect = null;    
                                
                                    private function __constructor() {
                                    }
                                
                                    public function getConnect() {
                                        if(!is_null($this->_connect)) {
                                              return $this->_connect;
                                        }
                                
                                        $this->_connect = new PDO('bla bla bla');
                                        return $this->_connect;
                                    }
                                
                                }

                                или статический метод
                                class psevdoDb {
                                
                                    private static $_connect = null;    
                                
                                    private function __constructor() {
                                    }
                                
                                    public static function getConnect() {
                                        if(!is_null(self::$_connect)) {
                                              return self::$_connect;
                                        }
                                
                                        self::$_connect = new PDO('bla bla bla');
                                        return self::$_connect;
                                    }
                                
                                }

                                Ну и т.д.
                                • UFO just landed and posted this here
                                    0
                                    Я бы сказал, что (публичные) статические методы нужны в тех случаях, когда они возвращают экземпляр класса.

                                    Например, именованные конструкторы:

                                    class User {
                                    private $login, $password, ...;
                                    public static function constructWithCredentials(Credentials $creds):User {
                                    $self = new self();
                                    $self->login = $creds->getLogin();
                                    $self->password = $creds->getPassword();
                                    return $self;
                                    }
                                    }
                                      0
                                      Как они упаковываються? к примеру:
                                            public static function getObject() {
                                                  new static()
                                            }

                                      Цитата:
                                      Одиночка (англ. Singleton) — порождающий шаблон проектирования, гарантирующий, что в однопоточном приложении будет единственный экземпляр класса с глобальной точкой доступа.

                                      Да статическими методами и параметрами класса реализуеться singleton, но эта реализация разрабатываеться программистом, а не статичным параметром/методом.
                                  0
                                  простой пример, одно соеденение с базой или 100, 100 пользователей и лимит соеденений превышен. Этот патерн довольно широко распространён в вебе. сессия, соеденение с б.д. классы которые пронести через 100 файлов и сохранить структуру. Да и лишних созданных объектов.
                                    0
                                    регитри и IoC/DIC. Коннекшен к базе — бывает такое что надо больше одного в рамках одного процесса. Сессия? нет сессий, или сессия в базе. Никаких сингелтонов.

                                    Проблема сингелтонов в том что это пожалуй самый простой в реализации паттерн и его юзат все кому не лень. Хотя по хорошему у вас просто не должно возникать в нем необхоздимости. Если вы зависимости до сих пор руками разгребаее — ну это ваша проблема. А контейнеры зависимостей делают это за меня и сами следят что не сделают 2 инстанса вместо одного.
                                0
                                Так какие принципы нарушают функции/статические методы? Все ооп — это типы и сокрытие состояния. Так что наш статический метод нарушает? Инкапсуляцию?

                                Статический метод не имеет никакого отношения к принципам ООП. Это "функция", которая имеет прямой доступ к состоянию объекта того типа, за которым она закреплена. Вот и все. Это не нарушает инкапсуляцию, так как детали реализации известны только члену объекта относящегося к данному типу.
                                  0
                                  статических методов просто нет в ООП.
                                  Там только объекты. Как можно выразить статический метод через взаимодействие через объекты?
                                    0
                                    Статический метод — это обычная функция (как правило чистая), которая имеет доступ к внутреннему состоянию объекта того типа, за которым она закреплена. Это непосредственно к ООП имеет прямое отношение, так как позволяет нам на каждый чих для любых мелких задач не делать отдельные методы геттеры предоставляющие доступ к состоянию объекта. Вот и все.
                                    Вся суть в удобстве. Статика — это хорошо, stateful статика плохо.
                                    Ну и да, если мы будем выражать все исключительно объектами и сообщениями между ними, если мы говорим о языке без ad-hoc полиморфизма, возможности делать различные варианты конструкторов и т.д. то наш код просто превратится в раздудую систему из объектов.
                                    А ведь все ООП вертится только вокруг сокрытия состояния от чужих глаз (в этом случае статика — не чужие глаза), и управление зависимостями.
                                  0
                                  Не нарушают они принципов сам по себе. Они могут нарушать, если программист захочет нарушать или не сможет не нарушать, но это относится и к обычным свойствам и методам.
                                    0
                                    В ООП просто нет этих сущностей. Возможно, я неправильно написал фразу, но суть моей претензии только в этом.
                                      0
                                      В классическом ООП нет и сущности "метод", тем не менее в мэйнстримовых ООП-языках, метод лишь разновидность функции (когда де-факто, а когда и "де-юре" как в PHP), а то и вообще обычная функция, которая называется методом, лишь потому что объект на неё ссылается.
                                        0
                                        Прошу прощения, но в ООП есть "метод" у объекта. Иначе как с ним работать?
                                          0
                                          в ООП есть только состояние + сообщения между объектами. Методы и их вызовы — это один из вариантов реализации.
                              0
                              Если уж говорить об ООП, то логично, говоря о состояниях, говорить об инстансах, а не о статике. Статика она на то и статика. Удовольствия мало. Так как к состояниям никакого отношения makeSomethingUseful не имеет. Так тихая миграция к паттернам JS. Верно. Нетихая.
                                0
                                у статики не должно быть состояния, но прямой доступ к состоянию инстансов оно иметь должно. В этом и соль. Если вам надо что-то сделать в рамках нескольких сущностей, и не совсем понятно кому эта операция должна быть доверена (например отсортировать коллекцию value object-ов) — то логично вынести это дело в статический метод. Тогда никаких разночтений и это весьма и весьма удобно.
                          +16
                          Вот я не то чтобы сторонник массивов, ООП это конечно правильно, но аргументы у вас как-то не очень способствуют пониманию преимуществ.
                          // Что такое 100, что такое "2013-10-04 12:16:47"?
                          // Понятно, что IDE помогает, но когда такие билдеры на пол-экрана,
                          // как-то надоедает мышкой по переменным водить, чтобы понять что тут происходит.
                          $pond->addStockData(new StockData("Bream", 100, "2013-10-04 12:16:47"));
                          
                          // Вот если массивом объявить, то сразу понятно, что 100 - это количество. Да и выглядит компактнее.
                          $pond = [
                              'name' => 'Breakspear',
                              'amenities' => ['shop'],
                              'fishBreeds' => [
                                  ['name' => 'Bream', 'number' => 100, 'stocked' => '2013-10-04 12:16:47'],
                                  ['name' => 'Perch', 'number' => 50, 'stocked' => '2012-02-02 05:23:32'],
                                  ['name' => 'Common Carp', 'number' => 10, 'stocked' => '2011-01-23 14:42:59'],
                              ],
                          ];
                          $ponds[] = $pond;

                          К тому же для объектов надо билдеры писать, а массив объявил и все. А если из базы грузим, то и объявлять не надо.

                          Намного меньше кода для получения того же результата.

                          Код будет такой же, только в функцию getNamed() добавится аргумент $ponds, а в getTotalStocked() аргумент $pond.

                          Часто это реализовывается как многофункциональные классы со здоровыми методами, использующими вложенные циклы.

                          А как вы без вложенных циклов посчитаете $stockedFish? Код будет аналогичный, один цикл по $ponds, второй в getTotalStocked($pond).

                          $ponds = new PondsCollection(); $ponds->add(...);

                          Зачем мне создавать коллекцию для методов типа add(), если я могу просто сделать $ponds[] = $pond? А если индексировать массив не числами, а первичными ключами сущности pond, то и получение или удаление можно сделать без всяких циклов, просто unset($ponds[$pond->id]).

                          Я хочу сказать, что такие аргументы только добавляют непонимания для тех, кто везде использует массивы вместо объектов.
                            +16
                            Массивы при всех их минусах имеют большой плюс — зайдя в средненький по качеству код с десятком разных структур (хранящихся в массиве) обычно понимаешь как эту структуру модифицировать после пары вардампов, интерфейс взаимодействия понятен и знаком любому программисту. Если упираться сильно в "правильный" ООП, то может получиться обратная ситуация — в коде куча структур тупо эмулирующих хранение неких данных, причем у каждой из этих структур автор кода радостно напридумывал методов ->add (ну, у одной add, у другой addItem, у третей addOne() — именование зависит от настроения, конечно) В результате — без подсказок IDE и изучения кода — шагу не ступишь. При доработках таких "классов" — обычно еще и наступает борьба с плохими абстракциями. Мораль какая? Везде должен быть баланс) не надо притягивать ООП за уши там где это не очень надо, надо смотреть на конкретную ситуацию.
                              +4
                              Полностью согласен, но найти ошибку в таком коде будет проблематично:
                              'fishBreeds' => [
                                      ['name' => 'Bream', 'numbr' => 100, 'stocked' => '2013-10-04 12:16:47'],
                                      ['name' => 'Perch', 'number' => 50, 'stocked' => '2012-02-02 05:23:32'],
                                      ['name' => 'Common Carp', 'number' => 10, 'slocked' => '2011-01-23 14:42:59'],
                                  ]
                              ```А их там аж 2...
                                0
                                Но для того чтобы от них избавиться не нужна ООП модель, достаточно объявить константы и использовать их, конечно будет не айс, но и не будет механических ошибок.
                                Ну и да, с именованным массивом всё не так ужасно, а вот если переписать это на обычный, и представить как с ним работать — вот это ещё тот адок — array + magic numbers много-много радости детишкам принесут...
                                  0
                                  'fishBreeds' => [
                                  ____['name' => 'Bream',_______'numbr' => 100, 'stocked' => '2013-10-04 12:16:47']
                                  ___ ,['name' => 'Perch',_______ 'number' => 50, 'stocked' => '2012-02-02 05:23:32']
                                  ___ ,['name' => 'Common Carp', 'number' => 10, 'slocked' => '2011-01-23 14:42:59']
                                  ]
                                  ```А их там аж 2…

                                  При правильном форматировании обе бросаются в глаза. (мда, без тегов выглядит уныло)
                                    0
                                    Пожалуйста, не нужно советовать так форматировать код, это пережитки прошлого.
                                    А вообще при добавлении нового массива, никто не будет набирать ключи с клавиатуры, их можно скопировать, или ctrl+d в ide.
                                    • UFO just landed and posted this here
                                        +1
                                        это пережитки прошлого

                                        а, ну извините, не держите зла.
                                  +4
                                  Безусловно в вашем посте есть здравый смысл, но во всем нужна мера.
                                  Может получиться так, что новичек прочтет ваш пост и начнет плодить сотни классов оберток, когда это нужно и не нужно.
                                  Не стоит забывать, что на объявление класса и тратится процессорное время и оперативная память (внутренее устройство сложнее).
                                  Доступ к данным поля объекта через вызов метода значительно медленнее, чем доступ к элементу массива, а если еще используются магические методы, разница огромна.
                                    +3
                                    «внутренее устройство сложнее»

                                    Пожалуйста, прекратите распространять этот миф времен PHP4.

                                    https://gist.github.com/nikic/5015323
                                      +2
                                      Вы невнимательно прочитали мой комментарий.
                                      В какой момент очевидные вещи стали мифами?
                                      Вот вам несколько тестов на скорость доступа к данным:
                                      test_array.php — использование массива
                                      test_object.php — использование класса-обертки
                                      test_object_magic.php — использование класса-обертки с магическим методом
                                      PHP 7.0.4
                                      Исходники
                                      Array:
                                      <?php
                                      $time = microtime(true);
                                      $data = [0,1,2,3,4,5,6,7,8,9];
                                      for($i=0;$i<500;$i++){
                                        $item = $data[rand(0,9)];
                                      }
                                      echo (microtime(true) - $time).' '.memory_get_usage()."\n";

                                      Object:
                                      <?php
                                      $time = microtime(true);
                                      
                                      class Example
                                      {
                                          protected $data = [0,1,2,3,4,5,6,7,8,9];
                                      
                                          public function getItem($index)
                                          {
                                            return $this->data[$index];
                                          }
                                      }
                                      
                                      $object = new Example();
                                      
                                      for($i=0;$i<500;$i++){
                                        $item = $object->getItem(rand(0,9));
                                      }
                                      
                                      echo (microtime(true) - $time).' '.memory_get_usage()."\n";

                                      Object + magic:
                                      <?php
                                      $time = microtime(true);
                                      
                                      class Example
                                      {
                                          protected $data = [0,1,2,3,4,5,6,7,8,9];
                                      
                                          public function __call($method , $params)
                                          {
                                            if($method == 'getItem'){
                                          return $this->data[$params[0]];
                                            }
                                          }
                                      }
                                      
                                      $object = new Example();
                                      for($i=0;$i<500;$i++){
                                        $item = $object->getItem(rand(0,9));
                                      }
                                      
                                      echo (microtime(true) - $time).' '.memory_get_usage()."\n";


                                      XDebug Profile


                                      Array: time: 0.006110 memory: 365072
                                      Object: time: 0.012696 memory: 366160
                                      Object + magic: time: 0.017254 memory: 366448
                                          0
                                          Эх, сложность алгоритмов оцениваеться не по времени выполнения, а в зависимости от входных данных, и операций над ними.
                                            0
                                            Про сложность алгоритмов я ничего не писал. А так, полностью согласен.
                                            0
                                            Из вашего комментария следует, что не только при использовании оберток, а вообще таки всегда, мой ответ относится только к этой части, я даже процитировал.

                                            А обертки — отдельный вопрос, как по мне, сами по себе они бессмысленны. ООП нужно не для того, чтобы писать анемичные обертки, а для инкапсуляции логики.
                                            0
                                            Если объявить плоский класс с публичными свойствами, то да, все будет отлично. Но это же не то, для чего придумывали ООП, верно? А в том случае, о котором говорит комментатор выше — доступ к данным через методы — оверхед все равно будет. Хорошо это или плохо, допустимо или недопустимо, наверное, нужно решать в каждом случае отдельно.
                                            • UFO just landed and posted this here
                                                0
                                                ООП придумали не для того, чтобы писать геттеры и сеттеры :-)
                                            –5
                                            В php я не силен и возможно пример в статье так себе, но я сразу начинаю думать о классах…
                                            Все же скажите, в PHP все действительно настолько плохо, что вы написали статью как крик отчаяния в безысходности?
                                              +2
                                              Да не всё нормально в PHP, просто когда на поддержке работаешь, особено после фрилансеров и веб студий, которые клепают сайты под ключ, довольно много непонятного кода. Но это во всех языках "кастыли" пишут и на c++, c, pascal, java, js, php в общем, "кастыли" "языконезависимые".
                                                0
                                                Вот и мне думается, что в самом языке все нормально. Больше всего меня удивило, что пост посвящен конкретному одному костылю в конкретном одном ЯП. При чем сам костыль заключается исключительно в теоретически неверно выбранном паттерне к какому-то конкретному случаю. Возникает вопрос: при чем тут массивы? Может просто «проблема» в том, что порог вхождения достаточно низок, чтобы часто наблюдать некачественный код?
                                                Жаль, что автор перевода не особо участвует в обсуждении.
                                              +3
                                              Возможно я и неправ, но массив как то понятнее, структуру данных сразу видно, не надо вникать в код классов чтобы понять как получить доступ к данным, да и вносить изменения, например добавить поле в массив проще, чем перелапачивать код классов и переписывать конструкторы инициализации и саму инициализацю в том числе.
                                                0
                                                Если это одномерный массив да. Но когда многомерный, и его можно в любом месте, приложения перезаписать $array['test'] = 3 к стати как то я 3 дня искал баг, и как раз из за этого. Если у вас объект вы можете защитить данные разграничить классы которые могут туда записать и т.д. и слёгкостью отследить в какой момент произошла запись вызов сеттера(исли он реализован).
                                                  0
                                                  Вы немного ушли от темы массивов к глобальным переменным. Массив вполне себе может находиться внутри объекта и быть недоступен извне.
                                                    –2
                                                    Да нет там не глобальная переменная была, там массив передовался в один метод, обрабатывался, возвращался и в другой метод. Где именно, произошла перезапись сложно отследить 'тогда я правда не пользовался xdebug', да и не всегда заметно баг. Да и тут, правильно люди говорят, что массив или объект должны использоваться по ситуации, если система построена на MVC с прослойками абстракции, и там по архитектуре приложения используються объекты, значит мы должны использовать объекты. Если удобно, и это вписываеться в архитектуру, то нужны массивы, если становиться неудобно то нужен объект. В общем всем должен управлять сдравый смысл.
                                                +7
                                                Ждем статью "Хватит злоупотреблять классами!"

                                                А по факту и то, и то — лишь инструмент и использовать его надо для подходящих для него задач.
                                                Серебряной пули нет и не было никогда.

                                                P.S. С классами то оёёй как можно переборщить, если везде тыкать их бездумно.
                                                  +4
                                                  Ждем статью "Хватит злоупотреблять классами!"

                                                  "Как перестать программировать и начать жить", чо уж там
                                                    +4
                                                    Я тоже жду. Ибо классы в отличии от массивов нельзя перепрограммировать. Статья уровня детского сада. Еще и MVC приплели. Вы еще вертолет обоснуйте.
                                                    0
                                                    Ждем статью «Хватит злоупотреблять классами!»
                                                    Не статья, но все же:
                                                    www.youtube.com/watch?v=o9pEzgHorH0
                                                  • UFO just landed and posted this here
                                                      –4
                                                      Вместо var_dump попробуйте настроить xdebug и все можно увидеть прямо в IDE и даже c удаленной машины.
                                                        –4
                                                        Не нашел ссылку на оригинал, прошу автора добавить к статье
                                                        Stop abusing arrays in PHP
                                                          +12
                                                          1001 комментарий на Хабре человека, который не может найти в переводе ссылку на источник.
                                                          Пора бы уже UI девелоперам Хабра задуматься.
                                                            0
                                                            Вот соглашусь. Да и сама структура ссылки на автора в виде "Перевод: %authorname%" запутывает. Получается, перевод сделал %authorname%? (по аналогии с "Графика: %artistname%", "Музыка: %composername%")
                                                            +3
                                                            image
                                                              0
                                                              Ну да, не указал самого главного....:(

                                                              Статья понравилась, спасибо за перевод!
                                                              0
                                                              Опа! Думал, в какой-то веки на хабре нормальная статья, а тут опять перевод!
                                                              +15
                                                              Прекрати злоупотреблять массивами в PHP
                                                              private $collection = array();
                                                              бдыщь
                                                                +1
                                                                "Прекрати злоупотреблять" != "Прекрати использовать".
                                                                • UFO just landed and posted this here
                                                                  +3
                                                                  А по мне, нет ничего плохого в массивах, если нам нужны данные без поведения, то бишь структуры. Такие, например, как конфиги.
                                                                  А если нужно поведение (примерно как в статье), то, конечно, лучше код инкапсулировать внутрь данных и получить объект.
                                                                  Не стоит эмулировать массивы объектами.
                                                                    +1
                                                                    Я бы так сказал.
                                                                    Нет ничего страшного в использовании массивов внутри границ классов или функций. Создавать массивы проще и удобней, и поддерживать код не сложно, т.к. все в одном месте.
                                                                    Не стоит использовать массивы как некий набор данных для передачи или возврата из функции. В этом случае массив "разбежится" по проекту, ключи могут составляться сложным образом(не дай бог), перетекать в другие массивы(array_merge), и тогда удачной отладки. В этом случае лучше создать структуру(т.е. класс)
                                                                      +6
                                                                      Кстати говоря, этот код бесполезная обертка над массивом, что она дает если в коллекцию можно добавлять элементы любого типа?
                                                                      class PondsCollection implements IteratorAggregate
                                                                      {
                                                                          private $collection = array();
                                                                      
                                                                          public function getIterator()
                                                                          {
                                                                              return new ArrayIterator($this->collection);
                                                                          }
                                                                      
                                                                          public function add($pond)
                                                                          {
                                                                              $this->collection[] = $pond;
                                                                          }
                                                                      }

                                                                      Если уж делать коллекцию определенного типа, то нужно же указать этот самый тип:
                                                                      class PondsCollection implements IteratorAggregate
                                                                      {
                                                                          private $collection = array();
                                                                      
                                                                          public function getIterator()
                                                                          {
                                                                              return new ArrayIterator($this->collection);
                                                                          }
                                                                      
                                                                          public function add(Pond $pond)
                                                                          {
                                                                              $this->collection[] = $pond;
                                                                          }
                                                                      }

                                                                      Вот теперь это определенный тип — коллекция Pond-ов.
                                                                      • UFO just landed and posted this here
                                                                        0
                                                                        Я бы перефразировал…
                                                                        «Прекрати злоупотреблять ООП в PHP»
                                                                        Всегда раздражало чрезмерное использование ООП даже там где оно вообще не нужно. (Привет блондинкам из SocialEngine, по меньшей мере версии 4. Это был ад.)
                                                                          –1
                                                                          Я сам сторонник классов. Но вот реалии таковы, что при переходе от массивов к классам начинает страдать производительность. В свое время убил немало времени, чтоб в одном из проектов увидеть это наглядно — и создание объектов, и итерации, и доступ к отдельным элементам — это все в классах полчается медленнее, чем в массивах. И пришлось откатить все от красивых классов к грубым массивам. Было это, правда, на 5.х, как обстоят дела в PHP7 не знаю.
                                                                          Так что вывод как всегда: для разных случаев — разные решения, а универсального рецепта не сущестует
                                                                            0
                                                                            используя массивы я всего лишь c помощью require могу подгружать конфиги и данные. и php opcache мне их закеширует
                                                                            этакое nosql-хранилище

                                                                            $config = require «config/app.php»;

                                                                            $getProducts = function () {
                                                                            foreach (require «db/products/index.php» as $product) {
                                                                            $product = require «db/products/». $product[«ID»]. ".php";
                                                                            //… do some work with $product;
                                                                            yeild $product;
                                                                            }
                                                                            };

                                                                            foreach ($getProducts() as $product) {
                                                                            print_r($product);
                                                                            //…
                                                                            }
                                                                              0
                                                                              Не могу согласиться. Замена массивов на объекты не даст ощутимых выгод, если в системе отсутствует ORM, и не проводилось хотя бы минимальное моделирование, а проблем прибавит:
                                                                              — в объектах рано или поздно появятся private\protected поля, в итоге их сохранение (напомню, у нас нет ORM) будет неудобным и громоздким
                                                                              — довольно быстро выявится потребность в ленивой загрузке, потому что поле данных\связь у объекта есть, но загружать её именно сейчас дорого\не имеет смысла
                                                                              — клонирование объектов со всеми вложенными сущостями не столь тривиально, по сравнению с массивами
                                                                              Вместо изначального тезиса, об использовании объектов, предложу использовать модель предметной области + какую-никаую orm.
                                                                                0
                                                                                Остается только подписаться под каждым словом. Единственное место, по хорошему, где могут появиться описанные деревья объектов — сериализатор/десериализатор для какого-либо API. Описанный же в публикации сценарий работы с БД — не что иное, как изобретение специфичного для проекта кривого и неполного подобия ORM.
                                                                                Кстати, в статически типизированных языках проблемы работа с такими деревьями не составляет: есть LINQ, линзы [https://github.com/julien-truffaut/Monocle] и прочие функциональные комбинаторы.
                                                                                +1
                                                                                Немого не понял смысла статьи:

                                                                                Использование массивов в PHP как единицы данных плохо.

                                                                                Нет не плохо, наоборот ассоциативные массивы, имхо, в PHP очень элегантны.

                                                                                Нужно инкапсулировать массивы в классы для удобства работы с ним.

                                                                                Конечно нужно!

                                                                                  0
                                                                                  > Нужно инкапсулировать массивы в классы для удобства работы с ним.

                                                                                  нет не нужно.
                                                                                  http://php.net/manual/ru/ref.array.php

                                                                                  посмотреть — в каждом долбаном фреймворке свои «удобные» классы для работы с массивами — да что они себе позволяют. возомнили себя умнее Расмуса…
                                                                                  • UFO just landed and posted this here
                                                                                      0
                                                                                      для поточной обработки сущесвуют генераторы с yeild
                                                                                      http://php.net/manual/ru/language.generators.overview.php — где можно уместить логику в одной функции

                                                                                      а итераторы на классах с кучей методов устарели и излишни.
                                                                                      • UFO just landed and posted this here
                                                                                          0
                                                                                          > мне приходится работать вплоть до ПХП 5.2

                                                                                          сочуствую. но у меня так получается — что клиенты с устаревшими движками под php 5.2 слишком мало платят и слишком многого хотят. поэтому задерживаются не долго.
                                                                                          а в основном неважно на чем там работает. главное чтоб запускалось. так что а я вот просто пишу новые проекты и функционал уже на php7, как в bitrix добавят поддержку вообще заживем.
                                                                                          для любителей старых версий получается просто дополнительные накладные расходы за перевод под старые версии php. имхо так и надо с клиентами иначе будут сидеть на старых ветках до скончания веков
                                                                                          • UFO just landed and posted this here
                                                                                              0
                                                                                              Знакомо с ошибками (включая свои собственные), но пути решения есть, даже если расчёты всегда производятся на лету, а не раз посчитанные результаты хранятся в БД. Как чисто технические пути (различные варианты реализации выбора алгоритмов расчёта в зависимости от, например, даты выставления счёта или заключения договора), так и административные — после исправления ошибки, выявляются проблемные сущности и бухгалтерия проводит по ним дополнительные начисления-списания. Сейчас вот переписываю проект с PHP5.3(Symfony1) на PHP7(Symfony3) и активно использую оба метода, предпочитая сначала попробовать вторым.
                                                                                  0
                                                                                  Подход с использованием паттерна object-value хорош, но только когда существует нормальная доменная модель. Заменять бездумно все многомерные массивы классами везде подряд, имхо, неоправданно. Многомерный массив может быть прекрасным наглядным примером, отражающим сразу и структуру данных и ее содержимое. Да, злоупотребрять не нужно, но и кардинально переписывать все на классы (так можно и заболеть ООП головного мозга) тоже не нужно. Самый правильный подход по моему мнению – максимальная простота и читаемость кода. Если массив улучшает понимание кода – он там должен быть.
                                                                                  • UFO just landed and posted this here
                                                                                    • UFO just landed and posted this here
                                                                                        +1
                                                                                        Это Матиас то не шарит? Может быть просто вы статью не поняли?)
                                                                                        Как уже было сказано, я не знаю, что значат эти числа. А что, если их нужно будет 10, или нужно поменять порядок параметров?

                                                                                        Значит вы не усвоили принцип protected variations и не обезопасили себя от гредующих изменений.
                                                                                        • UFO just landed and posted this here
                                                                                            0
                                                                                            У меня тоже ООП головного мозга, но это чуть другое ООП головного мозга нежели вы писали в своей заметке. По поводу оверюза паттернов и бездумного применения принципов (взять те же сеттеры у объектов, которые явно нарушают инкапсуляцию) я с вами полностью согласен. Люди как-то пытаются понять все эти SOLID-ы и т.д. не понимая зачем вообще придумали ОО.
                                                                                            В целом же лучше ОО только функциональщина, вот только это уже не про PHP.
                                                                                          –1
                                                                                          поддерживаю.

                                                                                          плюсанул бы — но кармы нет

                                                                                          эти любители ооп в конец распоясались — сколько хороших продуктов в стагнации из за той же simphony — drupal, phpbb и другие — вместо развития продукта и улучшения api — пытаются перестроить устоявшуюся структуру и впхнуть компоненты фрейморка — а в результаты получается франкеншейн
                                                                                            +1
                                                                                            Допустим, что в сообществах drupal, phpbb и других главенствуют (захватили власть?) любители ООП, которые пишут ООП-код ради ООП. Но что мешает не любителям ООП собраться вместе и убить всех любителей ООП и форкнуть эти проекты, развивая и улучшая их, а не рефакторить ради ООП?
                                                                                            Не получается? Что-то мешает? Процедурный код такого объёма не поддаётся анализу и ответственному рефакторингу? Нужен какой-то инструмент уменьшения сложности, абстрагирования, повторного использования с частичным переопределением или расширением, инкапсуляции и полиморфизма, чтобы проще было развивать и улучшать продукты?
                                                                                            • UFO just landed and posted this here
                                                                                                0
                                                                                                У всех мейнстримовых фрейморкох слишком много абстракций, из-за чего трудно сделать даже базовые вещи, не убив много времени на изучение фреймворка.

                                                                                                Ну а как без изучения, да и у фреймворков на подобие symfony, zf2, yii2 хорошо документированные методы, что позваляет cms подсвечивать как магические методы класса, так и обычные, выводя их описание и список параметров.
                                                                                                Да и что бы писать на языке яп, его сначала надо изучить как минимум синтаксическую базу, а потом писать. Это во всех ЯП так. Кроме php обычно на нём сначала начинаю писать а потом изучают.
                                                                                                • UFO just landed and posted this here
                                                                                                  • UFO just landed and posted this here
                                                                                                      0
                                                                                                      Вы приувеличиваете, в любой фреймворк/cms есть время вхождения, ну если не писать только кастыли, по этому не стоит говорить о "элементарных вещах" быстрее всё таки изучить->реализовать->не трогать, чем кастыль->баги->исправления->изучить->переписать->нетрогать. Я понимаю каждому своё, работад не давно над рукописной cms там без заморочек контроллер->данные->рендер и реализованно без кастылей и это круто всё понятно и быстро, но это только тогда когда всё хорошо написано и логично выстроенно.
                                                                                                      • UFO just landed and posted this here
                                                                                                          0
                                                                                                          С самописью взял бы и сделал и не мучался.

                                                                                                          Достаточно выкинуть Yii и использовать нормальные компоненты. Yii — очень упрощенный фреймворк для тех кто знает нужен он им или нет. С вашими запросами вам нужно что-то более гибкое, тот же Symfony или Zend. Или быстренько свой фреймворк на базе готовых компонентов собрать.
                                                                                                          • UFO just landed and posted this here
                                                                                                              0
                                                                                                              Скоро вроде будут переписывать.

                                                                                                              Весьма сомнительный бизнес-ход.
                                                                                                              как буду плеваться и на Symfony c Zend. :)

                                                                                                              У меня есть для вас совет — просто не завязывайтесь особо на фреймворк. Ну мол, что будет в контроллерах — плевать, а вот бизнес логика не особо то должна быть привязана к фреймворку. Да и штуки вроде роутеров и т.д. можно обойти. Я помниться на каком-то из проектов на Yii просто психанул и повесил свой маршрутизатор над Yii-ным, который обрабатывал 3-4 правила которые были нужны мне, и мне так было удобнее.
                                                                                                              Для личных проектов есть ядро, которое писалось с моего начала программирования в 2008 году.

                                                                                                              Попробуйте выкинуть велосипед из легаси и взять что-то нормальное. Серьезно. Я понимаю что велосипеды это весело, сам баловался, но только мне уже надоело их писать.
                                                                                                              • UFO just landed and posted this here
                                                                                          • UFO just landed and posted this here
                                                                                            • UFO just landed and posted this here
                                                                                              • UFO just landed and posted this here
                                                                                                • UFO just landed and posted this here
                                                                                              +3
                                                                                              Статья ну уж слишком провокационна и холиварна. Таки фразы убивают:

                                                                                              «Единственные причины, по которым разработчики продолжают писать подобный код — это или неопытность или потерянность в процедурном прошлом.»

                                                                                              Одним предложением поставили диагноз тысячам программистам (теперь уже получается говнокодерам).

                                                                                              А меня наоборот тошнит от злоупотребления ООП. И если я увижу код, где вместо обычного массива разработчик сделал объект, раздув тем самым код, я выпадаю в осадок. Это однозначно усложнение кода на ровном месте. Фразы про то, что в будущем такой код будет легче поддерживать — это сказки. В реальности злоупотребление ООП приводит к появлению в проекте десятков и сотен мини-классов, часть из которых взаимозависимы, что еще сильнее усложняет код. И ладно, если бы эти сотни классов делали бы что-то полезное. Но они всего лишь заменяют массивы! Чума. Я не против ООП и сам его использую, но заменять массивы точно бы не кому не советовал.

                                                                                              Сложный код обычно пишут именно программисты-новички. А те, кто уже съел собаку на распутывании спагетти-кода, стараются код сделать проще. KISS-идеология — наше все.
                                                                                              • UFO just landed and posted this here
                                                                                                  0
                                                                                                  Ох уж эти невидимые аргументы :)
                                                                                                  • UFO just landed and posted this here
                                                                                                  0
                                                                                                  Наверное, правильнее сказать — не злоупотребление ООП, а злоупотребление объектами. ООП подразумевает инкапсуляцию логики, а не бездумные обертки с геттерами-сеттерами. Я бы сказал, что без SOLID ООП — это не ООП.

                                                                                                  ООП — это не когда вместо $var['foo'] пишется $var->foo или $var->getFoo(). ООП — это когда пишется $this->foo.
                                                                                                  0
                                                                                                  Использование массивов само по себе ни хорошо, ни плохо. Плохо (в большинстве случаев) в настоящее время использовать в PHP массивы в качестве сишных структур, в качестве коллекций семантически разнотипных значений, особенно с ключами представляющими собой человеческие имена типа 'first_name' или 'contracts' — для этого есть объекты. А вот использовать массивы как коллекции однотипных значений с ключами в виде числовых индексов или идентификаторов объектов — хорошо.
                                                                                                    0
                                                                                                    А чем описанное в статье использование PHP-массивов принципиально отличается от JSON, который сейчас, вроде как, везде принято любить? По-моему, это абсолютно то же самое, этот код можно даже вынести в отдельный файл (с расширением .phpon?) или хранить в БД, «декодируя» вызовом функции eval. Поэтому, думаю, определённая легитимная область применения у этого подхода есть, частично совпадающая с областью применения JSON/YAML/XML.
                                                                                                      0
                                                                                                      eval зло, взломал базу->дописал shell->зашёл на сервер-> и понеслась
                                                                                                        0
                                                                                                        Само собой, зло — потому и кавычки. Но концептуально «PHP Object Notation» (или «PHP Array Notation»?) ничем не хуже JSON, кроме отсутствия аналога функции json_decode.
                                                                                                        0
                                                                                                        плохо — анемичная модель, плохо — отсутствие инкапсуляции и глобальное состояние. А все остальное — детали.
                                                                                                          0
                                                                                                          «Анемичная» модель в виде массива в качестве сущности бизнес-логики, наверное, совсем не айс, но в качестве DTO — почему нет? DTO не особенно нужна инкапсуляция, а хранить массив таких объектов в глобальной переменной никто не заставляет.
                                                                                                          Кстати, в Python есть такая вещь, как collections.namedtuple, которая очень подходит для сущностей сложнее кортежа, но недостаточно сложных для написания отдельного рукотворного класса. Уверен, что в насквозь динамическом PHP можно сделать аналог (или он уже существует).
                                                                                                            0
                                                                                                            но в качестве DTO — почему нет?

                                                                                                            Для DTO массивы — ок, во всяком случае в простых случаях, коих большинство. Но это никоем образом не относится к тому, о чем так печется Матиас (автор статьи, тут перевод если что) когда говорит обо всем этом — модель предметной области.
                                                                                                            но недостаточно сложных для написания отдельного рукотворного класса.

                                                                                                            Обычно все же эту штуку используют в качестве базового класса. Ну то есть для большинства случаев объявление нашей структуры сводится к одной вполне себе читабельной строчки, но если что — можно поведение добавлять и т.д. В PHP к сожалению таких механизмов нет.
                                                                                                            Уверен, что в насквозь динамическом PHP можно сделать аналог (или он уже существует).

                                                                                                            PHP то насквозь динамический, но не настолько к сожалению. Тут поможет только кодогенерация в рантайме. Всему виной различия в объектной модели php и python, у php оно более статично, в силу исторических причин и потому что слизано у java/.net.
                                                                                                            Во всяком случае я искал в свое время и не нашел, и понял что объявить простые структурки и сгенерировать конструктор как-то выходит быстрее и проще.
                                                                                                            p.s. вообще ооочень много вопросов и споров в этой статье свелись бы на нет, если бы автор перевода предоставил информацию о том, кто такой Матиас, с каким проектами он работает и т.д. Ибо заметки в его личном блоге обычно читают люди, кто вкурсе, что он обычно вещает о том как правильно проектировать предметную область, прославляет DDD и тд. А что у вас между контроллерами и моделью ходит в качестве DTO — массивчики динамические, статические структурки или тупые объекты без поведения — это уже вам решать.
                                                                                                            • UFO just landed and posted this here
                                                                                                        –1
                                                                                                        Эх поздно я вашу заметку увидел. Не дает мне Хабр ее плюсануть уже.
                                                                                                        Солидарен на 100%. Тоже всегда вызывает недоумение.
                                                                                                        Самое интересное, что ассоциативные контейнеры есть ведь во многих языках. Но только в php придумали использовать их вместо объектов. И не просто придумали, это стало просто эпидемией какой-то. Пандемия.
                                                                                                          0
                                                                                                          да ладно вам, в javascript сообществе с этим похуже как-то пока. Но ребята растут, развиваются. А у PHP-ников пока отсутствует понятие "состояни" и "побочные эффекты".
                                                                                                            0
                                                                                                            Придумали в то время, когда ещё объектов не было. И натолкнули на мысль авторы языка прежде всего, предоставляя окружение и запрос в виде суперглобальных ассоциативных массивов. Хочешь написать hello $name — используй массив.
                                                                                                            0
                                                                                                            Есть json структура, есть схема и есть набор бинарных буферов или мемори таблицы- больше ничего не надо! Если надо собрали вJSON объект из массивов ссылок на мемори таблицы. Если надо разобрали объект на массивы из ссылок на мемори таблицы или буферы в соответствии со схемами. Всё! Остальное извращение. Не имеет значения PHP,JS, JAVA,C++.
                                                                                                              +1
                                                                                                              А поведение где? Как вы будете спасаться от сайд эффектов? Из вашего описания получается какая-то процедурщина.
                                                                                                              Вообще есть хорошее правило — не стоит быть столь категоричными. Для каждой ситуации свой подход.

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