Використання php-генераторів, народне програмування

Використання php-генераторів, народне програмування
Незважаючи на те, що php-генератори доступні з php 5.5.0, вони все ще майже не використовуються. Більш того, більшість розробників, яких я знаю, розуміють, як працюють генератори. але не бачать, коли вони можуть бути корисні в реальному житті.

Так, генератори виразно виглядають добре, але знаєте. Я не розумію, де вони можуть бути корисними для мене, хіба що для розрахунку послідовності Фібоначчі.

І вони не помиляються, адже навіть приклади в php -документаціі занадто спрощені. Вони тільки пояснюють, як ефективно реалізувати range або ітерованих по рядках файлу.

Але навіть з цих простих приклад ми можемо зрозуміти ключові переваги використання генераторів. вони просто спрощують ітератори.

Генератори дозволяють вам написати код, який використовує foreach для ітерації безлічі даних без необхідності виділення пам'яті під масив.

Тримаючи цей факт в пам'яті, я спробую пояснити, чому генератори наскільки здорово допомогли мені вирішити завдання над якими я працював в компанії.

Спочатку трохи контексту

У більшості прикладів коду далі я буду називати ці метадані $ ebooks.

Ітерація по великому безлічі даних

За традицією я мав би написати щось на кшталт:

Проблему легко побачити: чим більше книг, тим більше потрібно пам'яті для $ filteredEbooks.

Одне з рішень - створити ітератор, який би ітерованих $ ebooks і повертав відповідні. Але для цього нам потрібно було б створити новий клас, крім того, ітератори реалізується трохи втомлює. На щастя, з php 5.5.0 ми можемо використовувати генератори!

Так, рефакторинг методу getEbooksEligibleToWebReader для використання генератора дуже простий: замінюємо передачу значень в змінну $ filteredEbooks конструкцією yield.

Припустивши, що $ ebooks НЕ масив книг. а итератор, або генератор (навіть краще!), споживання пам'яті тепер буде константою, не має значення, скільки книг потрібно повернути, і ми впевнені, що книги будуть шукатися тільки коли реально знадобляться.

Бонус: RulerZ всередині використовує генератори. так що ми можемо переписати метод і залишитися з тієї ж оптимізацією по виділенню пам'яті.

Агрегація декількох джерел даних

Тепер розглянемо момент отримання $ ebooks. Я вам не сказав, але вони по факту приходять з різних джерел: реляційної БД і Elasticsearch.

Ми можемо написати простий метод, котрий агрегує ці два джерела:

Але ще раз, кількість споживаної пам'яті при використанні такого підходу дуже залежить від кількості книг, що зберігаються в базі даних і Elasticsearch.

Ми можемо почати використовувати генератори і повернути результат:

Так, звичайно, краще, але у нас все одно є проблема: наш метод getBooks виконує дуже багато роботи! Ми повинні розділити дві відповідальності (зчитування даних з БД і виклик Elasticsearch) в два методи:

Ви могли помітити використання yield from оператора (доступний з php 7.0), який дозволяє делегувати використання генераторів. Це ідеально, наприклад, для агрегації декількох джерел даних, які використовують генератори.

yield from оператор працює з будь-яким Traversable об'єктом, так що масиви і ітератори також можуть бути використані з цим оператором.

Використовуючи таку конструкцію, ми можемо агрегувати кілька джерел даних пару рядками коду:

Складна лінива (на вимогу) гидрации записів БД

Інший варіант використання генераторів - реалізація ледачою гидрации, яка може обробляти зв'язку.

Мені потрібно було імпортувати сотні тисяч замовлень з давньою БД в нашу систему, кожне замовлення містив кілька пунктів.

Наявність замовлень і "пунктів замовлень" було передумовою до того, що ми повинні зробити. Я написав метод, який повертає сгідрірование замовлення і при цьому не стає занадто повільним або ненажерливим.

Ідея злегка наївна: сджойніть замовлення з пунктами, і згрупувати замовлення і пункти замовлень в циклі.

Використовуючи генератори. мені вдалося реалізувати метод, який отримує замовлення з БД і приєднує відповідні пункти замовлення. Все це споживає стабільна кількість пам'яті. Генератор позбавив від потреби тримати всі замовлення і їх пункти: поточне замовлення - це все, що мені потрібно, щоб сагрегіровать всі дані.

Імітація асинхронних завдань

Останнє, але тим не менш важливе: генератори так само можуть бути використані для імітації асинхронних завдань. Поки я писав цю замітку, я натрапив на пост @nikita_ppv на таку ж тематику, і так як він перший реалізував генератори в php. я просто залишу посилання на його пост.

Він швидко пояснює що таке генератори і (в деталях) як ми можемо отримати переваги через те, що вони можуть бути перервані і відправляти / приймати дані для реалізації співпрограми і навіть багатозадачності.

Підводячи підсумок

  • . спрощені ітератори;
  • . можуть повертати необмежені обсяги даних без додаткового споживання пам'яті;
  • . можуть бути агреговані за допомогою делегування генераторів;
  • . можуть бути використані для реалізації багатозадачності;
  • . просто прикольні!

Схожі статті