Получи случайную криптовалюту за регистрацию!

Beer::PHP 🍺

Логотип телеграм канала @beerphp — Beer::PHP 🍺 B
Логотип телеграм канала @beerphp — Beer::PHP 🍺
Адрес канала: @beerphp
Категории: Технологии , Образование
Язык: Русский
Количество подписчиков: 3.05K
Описание канала:

Здесь публикуются короткие заметки о PHP, Linux, Unit Testing, DB, OOP, etc., выдержки из статей, книг, видео, курсов и других материалов.
Теперь тебе больше не нужно перерывать тонны информации ;)
@genkovich — написать автору канала.

Рейтинги и Отзывы

3.33

3 отзыва

Оценить канал beerphp и оставить отзыв — могут только зарегестрированные пользователи. Все отзывы проходят модерацию.

5 звезд

1

4 звезд

0

3 звезд

1

2 звезд

1

1 звезд

0


Последние сообщения 7

2021-05-18 09:35:00 Статические анонимные функции

Буквально на днях пришел вопрос от одного из подписчиков касательно контента этого поста. Звучал он так: "А зачем делать callback’и в функции сортировки (usort), статическими?" И я подумал, что это действительно хороший вопрос, на который стоит обратить внимание.

Из документации:
При объявлении в контексте класса, текущий класс будет автоматически связан с ним, делая $this доступным внутри функций класса. Если вы не хотите автоматического связывания с текущим классом, используйте статические анонимные функции.

Выходит, что когда closure объявляется в контексте класса, то класс автоматически привязывается к замыканию. Это означает, что $this доступен внутри области анонимной функции. Вот такой вот пример.

На первый взгляд "да и чёрт с ним", но стоит копнуть чуть глубже.

Замыкание, содержащее ссылку на $this, может предотвратить сборку мусора для этого объекта, что, в свою очередь, может существенно повлиять на производительность. Вот примеры с использованием static и без него. Ну и gist, чтобы самостоятельно поиграться.

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

#php #middle #memory #source
963 viewsКирилл Сулимовский, 06:35
Открыть/Комментировать
2021-05-14 10:30:02 Использование индексов в MySQL

Чем больше мы пользуемся ORM, тем меньше задумываемся об оптимизации БД, до тех пор пока не прижмёт. В простых кейсах для ускорения запроса проблем не возникает, но если случай чуть сложнее чем "добавить индекс", то разработчики часто не знают за что хвататься. Здесь хочу оставить пару заметок, которые могут натолкнуть на различные решения в подобной ситуации.

Все мы знаем, что индексы используются для быстрого поиска строк с определенными значениями столбцов. Без индекса MySQL будет начинать поиск с первой строки, а затем читать всю таблицу. Чем больше таблица, тем дороже эта операция.

На что обратить внимание при оптимизации?

• Не исключен случай когда одна колонка используется в нескольких индексах. В таком случае MySQL выбирает индекс который вернет наименьшее кол-во строк (наиболее избирательный).

• При использовании составного (композитного) индекса помните, что он может использоваться и в более простых выборках, но только по столбцам перечисленным слева направо. Например индекс (col1, col2, col3) будет работать для выборок (col1), (col1, col2), и (col1, col2, col3), но не будет для (col2, col3) или (col3).

Для получения строк из других таблиц при JOIN

• Для сравнения строковых столбцов оба столбца должны использовать одну и ту же кодировку. Например, сравнение столбца utf8 со столбцом latin1 исключает использование индекса.

• MySQL может использовать индексы более эффективно, если они одного и того же типа и размера. В этом контексте VARCHAR и CHAR считаются одинаковыми, если они объявлены с одинаковым размером. Например, VARCHAR (10) и CHAR (10) имеют одинаковый размер, а VARCHAR (10) и CHAR (15) - нет.
Сравнение столбцов разного типа (например, VARCHAR с DATETIME или INT) может препятствовать использованию индексов, если при этом необходимо преобразование. Например в одной таблице у вас INT 1, а в другой VARCHAR ' 1' или '00001'.

Не слишком очевидное

• Индексы менее важны для маленьких таблиц или для больших, из которых нам нужно извлечь все данные или большую их часть. В таком случае последовательное чтение выполняется быстрее, чем при работе с индексом. Всё потому, что последовательное чтение минимизируют поиск на диске, даже если нам нужны не абсолютно все строки.

• Оптимизатору можно задать подсказку по выбору или игнорированию индекса.

SELECT * FROM table1 USE INDEX (col1_index,col2_index)
WHERE col1=1 AND col2=2 AND col3=3;

SELECT * FROM table1 IGNORE INDEX (col3_index)
WHERE col1=1 AND col2=2 AND col3=3;

Ставь если было полезно и если хочешь еще заметок по этой теме.

#MySQL #junior #source
369 viewsКирилл Сулимовский, edited  07:30
Открыть/Комментировать
2021-05-12 10:30:01 Сеть — ненадёжна

Давайте договоримся о том, что "Сеть — ненадёжна", примем это как аксиому, как фундаментальное правило, как гравитацию. Сегодня, в эпоху (микро)сервисов, огромного кол-ва сторонних решений вызываемых по API это очень важная аксиома.

Во время разработки нужно об этом помнить и задавать себе вопросы:

• А что если я получу HTTP timeout exception?
• Что делать, если я пошлю команду на изменение и не получу ответ?
• Является ли данный вызов идемпотентным?
• etc.

Что делать, раз вызов по сети может зафейлиться?

Мы должны обеспечить автоматический retry. Для этого отлично подходят системы управления очередями.

Такое взаимодействие называется store and forward, при котором информация попадает в промежуточное хранилище и позднее отправляется в конечный пункт назначения. Он также помогает обеспечить гарантированную доставку, поскольку сообщения сохраняются и никогда не теряются. Если наш получатель находится offline или возникли любые другие сетевые проблемы — система очередей должна попробовать повторить отправку сообщения.

Конечно стоит помнить об особенностях данного подхода. Для конечного пользователя взаимодействие перестанет быть синхронным. С одной стороны, нам не нужно заставлять пользователя ждать, пока выполнятся все наши сетевые вызовы, но с другой мы не можем сразу вывести пользователю конечный результат. То есть мы переходим от привычного Request/Response к Fire and Forget паттерну.

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

#middle #architecture #source
359 viewsКирилл Сулимовский, 07:30
Открыть/Комментировать
2021-05-09 12:30:00 Сегодня спешу вас порадовать первым гостевым постом (не рекламой). Надеюсь, что этот эксперимент будет успешным и время от времени здесь будут публиковаться и интересные материалы от подписчиков.

Unit testing (предисловие, part - 0)

Основная цель юнит тестирования — обеспечение стабильного роста проекта. Не быстрого, не сверхнадёжного, а стабильного.

В начале жизни продукта, писать его очень легко. Фичи пилятся, запускаются и интегрируется в код быстро, пользователи довольны, бизнес считает прибыль. Но со временем, сложность проекта растет, внедрение фич занимает всё больше времени, а руки при релизе начинают дрожать.

Чем больше вы пишете кода, тем сильнее растет так называемая "программная энтропия" (научный термин между прочим!) — степень беспорядка в системе. Соответственно скорость разработки продукта падает, потому как каждый раз когда вы трогаете код, энтропия увеличивается.

Что делать? Есть 2 варианта.

Можно забить если это:
1. Проверка идеи или MVP продукта, который слеплен из веток и сами знаете чего.
2. Сайт лендинг / визитка / каталог в которому не требуется длительный саппорт.
3. Проект, который существует только у вас на компе и не пойдет в продакшен.

Нужно контролировать если:
1. Продукт прошел стадию MVP и перерос в бизнес.
2. Нужен длительный саппорт.

Причем тут тесты? Они помогают контролировать. Как? Об этом в следующем посте.

#intern #unit #testing #author
422 viewsКирилл Сулимовский, 09:30
Открыть/Комментировать
2021-05-07 10:30:01 Как хранить UUID в MySQL?

К сожалению реалии таковы, что просто написать "никак" не получится, т.к. MySQL всё еще занимает лидирующие позиции по использованию в веб разработке, не смотря на все его недостатки.

Многие по-умолчанию сохраняют UUID в CHAR(36) и не переживают по этому поводу. Действительно, если кол-во записей в вашей таблице < 50к, то вы скорее всего не заметите никаких проблем. Но что если в какой-то из таблиц их больше полумиллиона?

Хранить как BINARY.

Благо с выходом 8й версии MySQL часть танцев с бубном была перенесена в коробку и UUID_TO_BIN / BIN_TO_UUID уже делают всё за тебя. Они производят сжатие 32 символов (36 или более с разделителями) до 16-битного формата или обратно до формата, который снова можно прочесть.

INSERT INTO t VALUES(UUID_TO_BIN(UUID()));
SELECT BIN_TO_UUID(id) FROM t;

Мы уже получим буст, и очень неплохой, т.к. MySQL отлично индексирует BINARY, даже лучше, чем привычный автоинкрементный INT. И всё бы ничего, если бы не кластерный индекс, который как раз и используется у PRIMARY KEY. Это значит, что для его оптимизации мы должны иметь и упорядоченные UUID в базе. Но как это сделать?

Для этого можно использовать UUID первой версии, в которой содержатся биты связанные со временем. А во время использования UUID_TO_BIN / BIN_TO_UUID использовать второй (необязательный, логический) аргумент:

INSERT INTO t VALUES (UUID_TO_BIN (UUID (), true));

Именно он переупорядочит биты, связанные со временем, так, чтобы последовательные сгенерированные значения были упорядочены и в индексе.

Ну и самое интересное — сравнение!

Выходит, что при малом кол-ве записей, наш оптимизированный UUID работает медленнее остальных, при этом при 500к он обгоняет по показателям INT. И здесь возникает совсем другая история, а именно кол-во потребляемой памяти для большого кол-ва записей :)

Безусловно, генерируемые id — довольно удобный инструмент, но всегда следует хорошо спроектировать решение в голове, прежде чем внедрить.

#PHP #MySQL #middle #source
542 viewsКирилл Сулимовский, edited  07:30
Открыть/Комментировать
2021-05-06 10:30:06 Как сохранять Value Objects (VO) в Doctrine?

Первое, что приходит на ум — использовать VO только на границах (входе и выходе), а внутри самой Entity использовать простые типы.
Этот подход может показаться странным, но если присмотреться — он вполне оптимальный, ведь для внешнего мира объект выглядит идеально.

Вариант чуть сложнее - использовать DBAL Types, для этого руками для такого типа нужно описать 2 метода:

convertToDatabaseValue($value, AbstractPlatform $platform)
convertToPHPValue($value, AbstractPlatform $platform)

Этот метод отлично подходит, если ваш VO хранит в себе одно значение, которое вам нужно вписать в одну колонку. Но что, если их несколько?

Embeddables

Данный подход, позволяет встраивать класс, не являющийся сущностью, внутрь сущности. Кажется она просто предназначена для хранения объектов-значений!

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

С одной стороны последний метод с использованием Embeddables выглядит очень громоздким, с другой первый метод хоть и простой, но мы внутри сущности добавляем поведение лишь для того, чтобы сохранить state (не криминально, но вызывает двоякие чувства). Кто-то обязательно скажет насчет аннотаций в сущности, которые "смешивают слои" (хотя я считаю, что т.к. аннотации это по сути комментарии, то сущность от этого не страдает), что стоит хранить подобное описание в XML или YAML.

Это нормально использовать симбиоз вышеописанных подходов. Всё зависит от задачи, времени на разработку и ваших договоренностей в команде.

#PHP #middle #source
532 viewsКирилл Сулимовский, 07:30
Открыть/Комментировать
2021-04-30 09:35:00 Когда следует использовать коллекции?

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

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

Как понять, что лучше всё таки использовать коллекцию, а не массив?

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

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

#PHP #junior #source
732 viewsКирилл Сулимовский, 06:35
Открыть/Комментировать
2021-04-28 10:30:01 Где определять ID сущности (entity)?

Здесь мы опустим часть, в которой мы сравниваем автоинкременты из БД и генерируемые id (uuid) и сразу перейдем ко вторым. Если останутся вопросы, то обязательно пробежимся и по теме сравнения.

Мы знаем 2 правила:
1. Сущности (Entity) должны иметь идентификатор.
2. Объекты должны создаваться валидными.

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

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

Чтобы не разбрасывать по всему проекту код самой генерации - его можно вынести в репозиторий сущности.

Сразу получим несколько преимуществ:

1. Концептуально логично: Репозиторий управляет сущностями и их id.
2. В случае необходимости - легко подменить реализацию т.к. код генерации в одном месте.
3. Можете даже использовать инкрементные id, если БД поддреживает последовательности (sequences, напр. postgresql).
4. Всё на своих местах, интерфейс и сущность в доменном уровне, uuid в инфраструктуре, как и полагается зависимым от даты / времени и случайных данных строкам.

#php #middle #advice #source
827 viewsКирилл Сулимовский, 07:30
Открыть/Комментировать
2021-04-26 10:30:02 Массивы (часть 1, использование массивов как списков)

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

Все элементы массива должны быть одного типа @var array .
Это касается как использования объектов внутри списков, так и простых типов (int, string и т.д.).

Старайтесь не использовать индекс вашего списка.
Не стоит полагаться на индексы, которые автоматически генерирует PHP или использовать их напрямую. Вам должно быть достаточно того, что массив итерируемый (foreach) и его кол-во элементов можно посчитать (count()). Следовательно нужно стараться избегать конструкции for.

Вместо удаления элементов используйте фильтр.
Это правило вытекает из предыдущего. Вы можете удалить элементы из списка по их индексу (unset()), но вместо этого лучше использовать array_filter() для создания нового списка без нежелательных элементов. Также не нужно использовать флаги, которые позволяют внутри array_filter() работать с индексами.

Эти правила помогут вам избавиться от лишних if, повысят читабельность и предсказуемость, а что нам еще надо то? :)

#php #array #junior #source
751 viewsКирилл Сулимовский, 07:30
Открыть/Комментировать
2021-04-23 10:30:01
Простой способ готовить коллекции

Если вы используете итерируемые объекты (например создаете коллекции) в PHP, то вот маленький лайфхак.

Вместо того, чтобы самостоятельно реализовывать все методы интерфейса Iterator (next(), current(), key() и т. д.), вы можете реализовать интерфейс IteratorAggregate с одним единственным методом getIterator(), результатом которого вернуть экземпляр ArrayIterator на основе вашего внутреннего массива.

Сложно объяснил, но на примере будет проще ;)

#PHP #junior #advice #source
750 viewsКирилл Сулимовский, 07:30
Открыть/Комментировать