#PHP #advice
Вы можете использовать
Это также может вам помочь, если данные для провайдера вы берете из файла.
Вы можете использовать
yield в PHPUnit dataProviders. Это может немного повысить их читабельность.Это также может вам помочь, если данные для провайдера вы берете из файла.
🔥1
#PHP #advice
Говорить, что объекты в PHP передаются по ссылке не совсем правильно.
В действительности, если объект передать в функцию и там изменить его состояние, то во внешней области видимости мы действительно получим измененный объект. Но если (без &) попытаться его обнулить (= null), то он не удалится и не изменится во внешней области видимости.
Всё дело в том, что при передаче объекта, как аргумента функции, он не является ссылкой(!). Объект внутри содержит копию идентификатора, который указывает на один и тот же объект.
В то же время, если указать амперсанд (&), то при изменении переменной внутри (присвоив тот же null), изменится переменная и снаружи.
Подробную инфу, с примерами, можно прочесть в документации.
Говорить, что объекты в PHP передаются по ссылке не совсем правильно.
В действительности, если объект передать в функцию и там изменить его состояние, то во внешней области видимости мы действительно получим измененный объект. Но если (без &) попытаться его обнулить (= null), то он не удалится и не изменится во внешней области видимости.
Всё дело в том, что при передаче объекта, как аргумента функции, он не является ссылкой(!). Объект внутри содержит копию идентификатора, который указывает на один и тот же объект.
В то же время, если указать амперсанд (&), то при изменении переменной внутри (присвоив тот же null), изменится переменная и снаружи.
Подробную инфу, с примерами, можно прочесть в документации.
🔥1
В сознании PHPшников почему-то никак не может умереть md5.
Да, было много статей на тему «Все пароли нужно хранить, предварительно захэшировав их в MD5», но примерно в 2016 году это утверждение потеряло свою актуальность. Использование его для токенов и паролей, да еще и без "соли", это преступление.
Что же делать?
Для этого давно придумали две достаточно удобных функции password_hash() и password_verify(). Да, больше не надо самостоятельно писать функции для создания хешей, добавления соли, и проверки всего этого дела.
Также, для генерирования случайного токена можно использовать конструкцию
bin2hex() — Преобразует бинарные данные в шестнадцатеричное представление.
То есть выражение выше сгенерирует вам случайную строку длинной 32 символа.
Конечно, есть огромная куча сторонних библиотек, позволяющих решить подобные задачи. Но если вы не хотите париться, при этом хоть как-то заботитесь о безопасности приложения, то используйте готовые функции из коробки.
Ставь 🍺, если готов дать md5 умереть :)
Да, было много статей на тему «Все пароли нужно хранить, предварительно захэшировав их в MD5», но примерно в 2016 году это утверждение потеряло свою актуальность. Использование его для токенов и паролей, да еще и без "соли", это преступление.
Что же делать?
Для этого давно придумали две достаточно удобных функции password_hash() и password_verify(). Да, больше не надо самостоятельно писать функции для создания хешей, добавления соли, и проверки всего этого дела.
Также, для генерирования случайного токена можно использовать конструкцию
$token = bin2hex(random_bytes(16));radnom_bytes() — была добавлена в 7 версии PHP, умеет генерировать криптографически безопасные псевдослучайные байты.
bin2hex() — Преобразует бинарные данные в шестнадцатеричное представление.
То есть выражение выше сгенерирует вам случайную строку длинной 32 символа.
Конечно, есть огромная куча сторонних библиотек, позволяющих решить подобные задачи. Но если вы не хотите париться, при этом хоть как-то заботитесь о безопасности приложения, то используйте готовые функции из коробки.
Ставь 🍺, если готов дать md5 умереть :)
👍2
Маленький лайфхак о том, как получить кучу данных по API и не переживать о своём коде ;)
👉 Представим себе достаточно стандартную ситуацию, в которой вам нужно получить из внешнего API кучу однотипных сущностей. Например вам нужно получить из сервиса рассылок всех ваших подписчиков (это не реклама, а пример приближенный к реальности ;)).
Какое решение чаще всего приходит на ум? Что-то вроде этого.
❓ И вроде всё выглядит неплохо, но какие у этого кода минусы?
❌ Тут мы пытаемся получить все данные и сохранить их в один массив. К сожалению не можем угадать, сколько там в реальности может быть данных (например разница тестового и прода) и не знаем с какой скоростью их количество может увеличиваться. В свою очередь мы рискуем получить переполнение памяти, которое мы никак не контролируем. Да и код выглядит как-то не идеально.
👌 Но выход есть! Используем генераторы.
Генератор позволяет вам писать код, использующий foreach для перебора набора данных без необходимости создания массива в памяти, что может привести к превышению лимита памяти, либо потребует довольно много времени для его создания. И наш конечный код будет выглядеть примерно следующим образом.
Конструкция
Ставь 🍺 если было полезно или 🐒 если не используешь генераторы в коде.
#PHP #advice
👉 Представим себе достаточно стандартную ситуацию, в которой вам нужно получить из внешнего API кучу однотипных сущностей. Например вам нужно получить из сервиса рассылок всех ваших подписчиков (это не реклама, а пример приближенный к реальности ;)).
Какое решение чаще всего приходит на ум? Что-то вроде этого.
❓ И вроде всё выглядит неплохо, но какие у этого кода минусы?
❌ Тут мы пытаемся получить все данные и сохранить их в один массив. К сожалению не можем угадать, сколько там в реальности может быть данных (например разница тестового и прода) и не знаем с какой скоростью их количество может увеличиваться. В свою очередь мы рискуем получить переполнение памяти, которое мы никак не контролируем. Да и код выглядит как-то не идеально.
👌 Но выход есть! Используем генераторы.
Генератор позволяет вам писать код, использующий foreach для перебора набора данных без необходимости создания массива в памяти, что может привести к превышению лимита памяти, либо потребует довольно много времени для его создания. И наш конечный код будет выглядеть примерно следующим образом.
Конструкция
yield from позволяет вам развернуть полученный массив данных и вернуть каждый его элемент. Ставь 🍺 если было полезно или 🐒 если не используешь генераторы в коде.
#PHP #advice
👍1
#PHP #advice
Еще один вариант использования статического анализатора
Довольно распространенная ситуация, когда при рефакторинге, вам нужно поменять сигнатуру метода (например добавить или убрать один из параметров). После этого нужно найти все места, в которых этот метод вызывается и поправить вызов. На сегодняшний день IDE отлично справляется, но бывают моменты, когда вызовов очень много, название метода не уникально, при поиске результаты постоянно перемешиваются т.д.
Как же получить список мест, в котором нужно поправить измененный метод?
Если вы постоянно используете PHP Stan или Psalm, то после изменения существующей сигнатуры просто запустите свой статический анализатор и он с радостью вам предоставит этот список.
Безусловно, всё зависит от ситуации, но о таком варианте не стоит забывать ;)
Еще один вариант использования статического анализатора
Довольно распространенная ситуация, когда при рефакторинге, вам нужно поменять сигнатуру метода (например добавить или убрать один из параметров). После этого нужно найти все места, в которых этот метод вызывается и поправить вызов. На сегодняшний день IDE отлично справляется, но бывают моменты, когда вызовов очень много, название метода не уникально, при поиске результаты постоянно перемешиваются т.д.
Как же получить список мест, в котором нужно поправить измененный метод?
Если вы постоянно используете PHP Stan или Psalm, то после изменения существующей сигнатуры просто запустите свой статический анализатор и он с радостью вам предоставит этот список.
Безусловно, всё зависит от ситуации, но о таком варианте не стоит забывать ;)
👍1🗿1
Внедрение зависимостей (Dependency injection) и передача контекста (Context passing).
Для лучшего понимания и синхронизации предлагаю коротко рассмотреть эту тему. Не будем пока обсуждать плюсы и минусы, а просто проясним разницу между этими понятиями.
В качестве примера возьмём один из частых UseCase'ов - подтверждение почты пользователя.
Cценарий выглядит следующим образом:
1. Пользователь передает нам email.
2. Мы должны убедиться, что такой почты еще нет в нашей базе.
3. Генерируем токен для одноразовой ссылки.
4. Отправляем пользователю письмо на почту с ссылкой для подтверждения.
Чтобы сконструировать use case, нам понадобится repository (для проверки почты), генератор токенов и объект mailer для отправки письма. Завернём всё это в Dependency injection и теперь наш код выглядит следующим образом.
Что такое передача контекста (Context passing)?
Представьте, что все объекты в вашем приложении "вечные". Например,
Теперь рассмотрим приходящий
В идеале:
1. Отделяйте зависимости от контекста.
2. В метод передавайте только те значения контекста, которые использует данный метод.
#PHP #advice #middle #source
Для лучшего понимания и синхронизации предлагаю коротко рассмотреть эту тему. Не будем пока обсуждать плюсы и минусы, а просто проясним разницу между этими понятиями.
В качестве примера возьмём один из частых UseCase'ов - подтверждение почты пользователя.
Cценарий выглядит следующим образом:
1. Пользователь передает нам email.
2. Мы должны убедиться, что такой почты еще нет в нашей базе.
3. Генерируем токен для одноразовой ссылки.
4. Отправляем пользователю письмо на почту с ссылкой для подтверждения.
Чтобы сконструировать use case, нам понадобится repository (для проверки почты), генератор токенов и объект mailer для отправки письма. Завернём всё это в Dependency injection и теперь наш код выглядит следующим образом.
Что такое передача контекста (Context passing)?
Представьте, что все объекты в вашем приложении "вечные". Например,
mailer: мы можем отправить любое количество писем, вызывая метод send() и подставляя разные аргументы. Теперь рассмотрим приходящий
Request (откуда мы будем получать email). Его нельзя назвать "вечным" или "постоянным", он приходит и уходит, и всегда будет отдавать нам разные данные. Если нужен подобный объект, будь то Request, DTO (command), session и т.д., не вставляйте его в конструктор, а передайте его в качестве аргумента метода.В идеале:
1. Отделяйте зависимости от контекста.
2. В метод передавайте только те значения контекста, которые использует данный метод.
#PHP #advice #middle #source
🔥1
Слои (Layers), Infrastructure
После предыдущего поста поступило несколько просьб пройтись по теме слоёв
Об этом достаточно подробно написано в книгах ""Чистая архитектура"" Р.Мартина и DDD Эванса. Здесь мы будем рассматривать 3 базовых слоя — Infrastructure, Application и Domain.
Их можно выделить и больше, но этих достаточно, чтобы понять принцип.
Зачем они нужны?
Основными идеями выступают независимость вашего кода (от фреймворка, базы данных, внешних сервисов, UI и т.д.) и тестируемость.
Окей, звучит неплохо, что дальше?
Еще одно очень важное правило — "Правило зависимостей":
Ничто из внутреннего слоя не может знать что-либо о внешнем слое, ничто из внутреннего слоя не может указывать на внешний слой.
Пока сложно, но давайте перейдем к коду.
Слой
Экшен нашего контроллера может принимать request, делать валидацию для пользователя (про валидацию можно сделать отдельный пост) средствами фреймворка (или руками), создать DTO (команду) и запустить бизнес процесс (вызвать useCase / handler). Затем получит результат обработки данных и отправить пользователю информацию. В простом варианте контроллер выглядит так.
#PHP #layers #middle #source
После предыдущего поста поступило несколько просьб пройтись по теме слоёв
(Layers), чтобы у всех было понимание о чём идёт речь.Об этом достаточно подробно написано в книгах ""Чистая архитектура"" Р.Мартина и DDD Эванса. Здесь мы будем рассматривать 3 базовых слоя — Infrastructure, Application и Domain.
Их можно выделить и больше, но этих достаточно, чтобы понять принцип.
Зачем они нужны?
Основными идеями выступают независимость вашего кода (от фреймворка, базы данных, внешних сервисов, UI и т.д.) и тестируемость.
Окей, звучит неплохо, что дальше?
Еще одно очень важное правило — "Правило зависимостей":
Ничто из внутреннего слоя не может знать что-либо о внешнем слое, ничто из внутреннего слоя не может указывать на внешний слой.
Пока сложно, но давайте перейдем к коду.
Слой
Infrastucture cодержит в себе всё, что связывает ваше приложение с внешним миром, например: Получение HTTP запроса и отправка ответа, отправка запросов к другим серверам, отправка сообщений в очереди и т.д. Фреймворки обеспечивают нам все внешние взаимодействие из коробки, а нам остаётся правильно писать код в тех местах, в которых мы соприкасаемся с фреймворком — в большинстве случаев это контроллеры или консольные команды.Экшен нашего контроллера может принимать request, делать валидацию для пользователя (про валидацию можно сделать отдельный пост) средствами фреймворка (или руками), создать DTO (команду) и запустить бизнес процесс (вызвать useCase / handler). Затем получит результат обработки данных и отправить пользователю информацию. В простом варианте контроллер выглядит так.
$this->createUserHandler->excecute($command) - как раз обращается уже к слою Application, который мы рассмотрим в следующем посте.#PHP #layers #middle #source
🔥1
Слои (часть 2), Application and Domain
Слой
Data Transfer Object (DTO) — должен содержать только значения примитивных типов и не должен содержать поведения.
Зачем нужен 'use case' или 'command handler'?
Он точно знает как нужно обработать определенную команду. Внутри себя именно он ответственный за всю оркестрацию: здесь, в конструкторе происходит подключение всех необходимых объектов доменной области (это могут быть сущности, value objects, репозитории, генераторы данных и т.д.).
Handler использует данные из DTO например для создания сущности или получения данных из репозитория и выполнения некоторых действий по нему. В самом методе описывается та самая бизнесовая логика, которую должно выполнить ваше приложение.
Возьмём пример уже из существующего поста и дополним его. Как видите в новом примере в конструкторе мы подключили все необходимые зависимости, в execute передали контекст (DTO), а внутри метода содержится бизнесовая логика, прямо как в требованиях задачи: проверяем email на уникальность, генерируем токен, сохраняем в базу, шлем его на почту.
В отличие от предыдущего, этот слой отлично поддаётся Unit тестированию.
Что же относится к Domain?
• Entities
• Value objects
• Domain events
• Repository interfaces
• Domain services
• Factories
• ...
Код из этого слоя не должен никак соприкасаться с "реальным миром".
#PHP #middle #layers #source
Слой
Application, как видно из предыдущего поста, содержит классы command (DTO) и command handlers (например use case's или сервисы, в зависимости от подхода).Data Transfer Object (DTO) — должен содержать только значения примитивных типов и не должен содержать поведения.
Зачем нужен 'use case' или 'command handler'?
Он точно знает как нужно обработать определенную команду. Внутри себя именно он ответственный за всю оркестрацию: здесь, в конструкторе происходит подключение всех необходимых объектов доменной области (это могут быть сущности, value objects, репозитории, генераторы данных и т.д.).
Handler использует данные из DTO например для создания сущности или получения данных из репозитория и выполнения некоторых действий по нему. В самом методе описывается та самая бизнесовая логика, которую должно выполнить ваше приложение.
Возьмём пример уже из существующего поста и дополним его. Как видите в новом примере в конструкторе мы подключили все необходимые зависимости, в execute передали контекст (DTO), а внутри метода содержится бизнесовая логика, прямо как в требованиях задачи: проверяем email на уникальность, генерируем токен, сохраняем в базу, шлем его на почту.
В отличие от предыдущего, этот слой отлично поддаётся Unit тестированию.
Что же относится к Domain?
• Entities
• Value objects
• Domain events
• Repository interfaces
• Domain services
• Factories
• ...
Код из этого слоя не должен никак соприкасаться с "реальным миром".
#PHP #middle #layers #source
🔥1
Статический анализ. Psalm.
Если в 2021 вы еще не используете статические анализаторы, то срочно, нет, немедленно начинайте это делать :) Они решают просто миллион проблем, позволяют выполнять и контролировать десятки вопросов, но обо всём по порядку.
Что делают эти ваши анализаторы?
Анализируют текст программы и проверяют её на типичные (и не очень ошибки). При этом сам код не запускается (!). Примеры проверок, которые при этом выполняются:
• код не содержит синтаксических ошибок
• все классы, методы, функции, константы существуют
• переменные существуют
• в PHPDoc подсказки соответствуют действительности.
• проверяются неиспользуемые аргументы, переменные и свойства
• проверка типов данных
Круто, с чего начать использование Psalm?
После установки с помощью composer достаточно запустить команду
И вот момент истины, на нашем существующем проекте мы запускаем
Заботливые разработчики psalm сделали команду
Чтобы обновить состояние baseline достаточно выполнить:
#PHP #psalm #junior #source
Если в 2021 вы еще не используете статические анализаторы, то срочно, нет, немедленно начинайте это делать :) Они решают просто миллион проблем, позволяют выполнять и контролировать десятки вопросов, но обо всём по порядку.
Что делают эти ваши анализаторы?
Анализируют текст программы и проверяют её на типичные (и не очень ошибки). При этом сам код не запускается (!). Примеры проверок, которые при этом выполняются:
• код не содержит синтаксических ошибок
• все классы, методы, функции, константы существуют
• переменные существуют
• в PHPDoc подсказки соответствуют действительности.
• проверяются неиспользуемые аргументы, переменные и свойства
• проверка типов данных
Круто, с чего начать использование Psalm?
После установки с помощью composer достаточно запустить команду
./vendor/bin/psalm --init
Которая cгенерирует следующий файл. И вот момент истины, на нашем существующем проекте мы запускаем
./vendor/bin/psalm
И видим 1125 ошибок, которые очень лень править. У многих на этом заканчивается внедрение статического анализатора :) Но выход есть.Заботливые разработчики psalm сделали команду
./vendor/bin/psalm --set-baseline=your-baseline.xml
Которая запомнит текущие ошибки в вашем проекте и запишет их в указанный файл. Дальше вы можете спокойно запускать проверку и контролировать новый код, который пишете в вашем проекте. Чтобы обновить состояние baseline достаточно выполнить:
./vendor/bin/psalm --update-baseline
Конечно не стоит забывать и о старых ошибках, которые теперь можно постепенно исправлять не мешая основной разработке.#PHP #psalm #junior #source
👍3
Релиз PhpStorm 2021.1
8 апреля состоялся долгожданный релиз версии 2021.1. В ребята в очередной раз порадовали большим набором крутых фич:
• Куча подсказок и автозамен для упрощения и повышения читабельности кода
• Code with me - для совместной удаленной разработки и парного программирования (плагин, правда, был доступен ранее)
• Подлатали HTTP-Клиент
• Добавили превью PHP и HTML-файлов прямо в редакторе(!)
С полным перечнем изменений можно ознакомиться здесь или посмотреть видос.
#PHP #tools #junior
8 апреля состоялся долгожданный релиз версии 2021.1. В ребята в очередной раз порадовали большим набором крутых фич:
• Куча подсказок и автозамен для упрощения и повышения читабельности кода
• Code with me - для совместной удаленной разработки и парного программирования (плагин, правда, был доступен ранее)
• Подлатали HTTP-Клиент
• Добавили превью PHP и HTML-файлов прямо в редакторе(!)
С полным перечнем изменений можно ознакомиться здесь или посмотреть видос.
#PHP #tools #junior
Дженерики (Generics, templates) уже сегодня.
Наверное один из самых старых обсуждаемых и популярных RFC в PHP. На данный момент на уровне языка реализовать их трудно, как бы сильно разработчикам не хотелось получить этот инструмент.
Однако, используя Psalm (или PHPStan) в ваших проектах, вы можете делать это уже сейчас.
В чем смысл их использования?
Один из частых кейсов, когда нам нужно создать коллекцию объектов. Но создавая её таким образом, мы не можем гарантировать, что внутри все объекты будут типа
1. Забивает болт.
2. Внутри всех
3. Добавляет явную проверку
Для таких случаев у Psalm есть @template. Вот так может выглядеть ваша коллекция.
Теперь каждая коллекция ожидает аргумент своего типа. Это избавляет вас от лишних проверок, необходимости явно указывать
В случае несоблюдения этих контрактов, статический анализатор выкинет ERROR. Если вы пользуетесь PHPStorm, то он обязательно вам подскажет об ошибке и без запуска psalm.
Короче, этот инструмент отлично работает и давно используется многими разработчиками, так что не нужно ждать, а можно использовать его уже сейчас ;)
#PHP #middle #psalm #source
Наверное один из самых старых обсуждаемых и популярных RFC в PHP. На данный момент на уровне языка реализовать их трудно, как бы сильно разработчикам не хотелось получить этот инструмент.
Однако, используя Psalm (или PHPStan) в ваших проектах, вы можете делать это уже сейчас.
В чем смысл их использования?
Один из частых кейсов, когда нам нужно создать коллекцию объектов. Но создавая её таким образом, мы не можем гарантировать, что внутри все объекты будут типа
User. Что делает разработчик в таком случае?1. Забивает болт.
2. Внутри всех
foreach приходится добавлять явную аннотацию /** @var User $user */ и нужно следить не только за типами, но и за аннотациями.3. Добавляет явную проверку
$user instanceof User.Для таких случаев у Psalm есть @template. Вот так может выглядеть ваша коллекция.
@template T — всё равно что согласно RFC написать Collection<T>. Это означает, что любая ссылка на докблок T, должна рассматриваться как параметр этого типа. @psalm-param class-string<T> $type — указываем, что параметры должны быть строкой класса определенного типа.Теперь каждая коллекция ожидает аргумент своего типа. Это избавляет вас от лишних проверок, необходимости явно указывать
@var или создавать классы коллекций для каждого типа объекта.В случае несоблюдения этих контрактов, статический анализатор выкинет ERROR. Если вы пользуетесь PHPStorm, то он обязательно вам подскажет об ошибке и без запуска psalm.
Короче, этот инструмент отлично работает и давно используется многими разработчиками, так что не нужно ждать, а можно использовать его уже сейчас ;)
#PHP #middle #psalm #source
Object Mother pattern
Материнский объект — это класс, используемый при тестировании, который помогает создавать примеры объектов, которые вы используете для тестирования (М. Фаулер, 2006)
Этот шаблон помогает создавать определенные объекты, которые можно повторно использовать в нескольких тестах, что позволяет делать ваш код более читабельным.
По факту это броское название для фабрики, которая не только возвращает объект, но и задаёт его состояние.
❗️Конечно, к его созданию использованию нужно подходить с умом. Помните, что создавая методы под каждое состояние, можно очень сильно раздуть класс и превратить его в так называемый
Не смотря на определенные минусы данного подхода он может дать быстрый буст при внедрении тестов в ваш проект, или быстро описать давно устоявшиеся сущности.
❔Есть ли более крутая альтернатива? Да —
#PHP #testing #unit #source #junior
Материнский объект — это класс, используемый при тестировании, который помогает создавать примеры объектов, которые вы используете для тестирования (М. Фаулер, 2006)
Этот шаблон помогает создавать определенные объекты, которые можно повторно использовать в нескольких тестах, что позволяет делать ваш код более читабельным.
По факту это броское название для фабрики, которая не только возвращает объект, но и задаёт его состояние.
❗️Конечно, к его созданию использованию нужно подходить с умом. Помните, что создавая методы под каждое состояние, можно очень сильно раздуть класс и превратить его в так называемый
GodClass. Его будет сложно поддерживать и придётся страдать, если изменится поведение основной сущности.Не смотря на определенные минусы данного подхода он может дать быстрый буст при внедрении тестов в ваш проект, или быстро описать давно устоявшиеся сущности.
❔Есть ли более крутая альтернатива? Да —
Test Data Builders (пост будет чуть позже), но на старте она будет требовать больше усилий и времени. #PHP #testing #unit #source #junior
Оптимизация LIMIT / OFFSET
Также рассмотрим тему в рамках синхронизации (т.к. проблема стара и бояниста как этот мир) и в преддверии статейки про Cursor API.
❓Что плохого в LIMIT и OFFSET?
На малых объёмах данных никто и ничего не почувствует. Но долгоживущие проекты, в которые постоянно складываются данные рано или поздно сталкиваются с тем, что всё больше запросов появляется в slow.log, а запрос обычной постраничной пагинации отрабатывает 3-5 секунд(!).
Всё дело в том, что выполняя запрос типа
Рисунок наглядно изображает такую ситуацию. База читает первые 10 записей, после этого вставляется новая запись, которая смещает все прочитанные записи на 1. Затем база берет новую страницу из 10 следующих записей и начинает не с 11-й, как должна, а с 10-й, дублируя эту запись. Есть и другие аномалии, связанные с использованием этого выражения, но эта — самая распространенная.
⁉️О Боже, что же делать?!
Вместо комбинации OFFSET и LIMIT стоит использовать конструкцию
Конечно, у этого подхода могут есть свои ограничения: проблемно сортировать, нет нормальной возможности читать страницы непоследовательно и т.д. Однако он отлично подойдет для бесконечного скроллинга, или если вам нужно отдать большие объемы данных по API.
#mysql #junior #source
Также рассмотрим тему в рамках синхронизации (т.к. проблема стара и бояниста как этот мир) и в преддверии статейки про Cursor API.
❓Что плохого в LIMIT и OFFSET?
На малых объёмах данных никто и ничего не почувствует. Но долгоживущие проекты, в которые постоянно складываются данные рано или поздно сталкиваются с тем, что всё больше запросов появляется в slow.log, а запрос обычной постраничной пагинации отрабатывает 3-5 секунд(!).
Всё дело в том, что выполняя запрос типа
mysql> SELECT * FROM table_name ORDER BY id LIMIT 10 OFFSET 8000001;
Mysql сначала пройдется по первым 8000001 записям (которые нам не нужны) и только потом выберет нужные 10. И это еще не всё! Если между чтением двух страниц данных с диска другая операция вставит новую запись, что произойдет в этом случае?Рисунок наглядно изображает такую ситуацию. База читает первые 10 записей, после этого вставляется новая запись, которая смещает все прочитанные записи на 1. Затем база берет новую страницу из 10 следующих записей и начинает не с 11-й, как должна, а с 10-й, дублируя эту запись. Есть и другие аномалии, связанные с использованием этого выражения, но эта — самая распространенная.
⁉️О Боже, что же делать?!
Вместо комбинации OFFSET и LIMIT стоит использовать конструкцию
mysql> SELECT * FROM table_name WHERE id > 8000000 LIMIT 10;
Вот что мы можем выиграть по времени. Конечно, у этого подхода могут есть свои ограничения: проблемно сортировать, нет нормальной возможности читать страницы непоследовательно и т.д. Однако он отлично подойдет для бесконечного скроллинга, или если вам нужно отдать большие объемы данных по API.
#mysql #junior #source
👍1
Принцип подстановки Барбары Лисков
❓Почему у многих возникают проблемы с этим принципом? Если взять не заумное , а более простое объяснение, то оно звучит так:
«Наследующий класс должен дополнять, а не замещать поведение базового класса».
Звучит логично и понятно, расходимся. но блин, как этого добиться? Почему-то многие просто пропускают мимо ушей следующие строки, которые как раз отлично объясняют что нужно делать.
Рассмотрим выражение "Предусловия не могут быть усилены в подклассе"
❗️Другими словами дочерние классы не должны создавать больше предусловий, чем это определено в базовом классе, для выполнения некоторого бизнесового поведения. Вот несложный пример.
❌ Добавление второго условия как раз является усилением. Так делать не надо!
• Этот пример показывает, как расширение допускается, потому что метод Bar->process() принимает все типы параметров, которые принимает родительский метод.
• В этом примере дочерний класс может принимать более широкий спектр объектов, которые могут быть наследованы от
Таким образом, мы не добавляем дополнительных проверок, не делаем условия жестче и наш дочерний класс уже ведёт себя более предсказуемо.
В следующих постах мы также рассмотрим постуловия и ковариантность ;)
#PHP #middle #source
❓Почему у многих возникают проблемы с этим принципом? Если взять не заумное , а более простое объяснение, то оно звучит так:
«Наследующий класс должен дополнять, а не замещать поведение базового класса».
Звучит логично и понятно, расходимся. но блин, как этого добиться? Почему-то многие просто пропускают мимо ушей следующие строки, которые как раз отлично объясняют что нужно делать.
Рассмотрим выражение "Предусловия не могут быть усилены в подклассе"
❗️Другими словами дочерние классы не должны создавать больше предусловий, чем это определено в базовом классе, для выполнения некоторого бизнесового поведения. Вот несложный пример.
❌ Добавление второго условия как раз является усилением. Так делать не надо!
Контравариантность также можно отнести к данному приниципу. Она касается параметров функции, которые может ожидать подкласс. Подкласс может увеличить свой диапазон параметров, но он должен принять все параметры, которые принимает родительский. • Этот пример показывает, как расширение допускается, потому что метод Bar->process() принимает все типы параметров, которые принимает родительский метод.
• В этом примере дочерний класс может принимать более широкий спектр объектов, которые могут быть наследованы от
Money, не только Dollars.Таким образом, мы не добавляем дополнительных проверок, не делаем условия жестче и наш дочерний класс уже ведёт себя более предсказуемо.
В следующих постах мы также рассмотрим постуловия и ковариантность ;)
#PHP #middle #source
Принцип подстановки Барбары Лисков (part 2 — постусловия, ковариантность)
И сразу продолжение предыдущего поста — "Постусловия не могут быть ослаблены в подклассе".
То есть подклассы должны выполнять все постусловия, которые определены в базовом классе. Постусловия проверяют состояние возвращаемого объекта на выходе из функции. Вот такой пример.
❌Условное выражение проверяющее результат является постусловием в базовом классе, а в наследнике его уже нет. Не делай так!
Сюда-же можно отнести и ковариантность, которая позволяет объявлять в методе дочернего класса типом возвращаемого значения подтип того типа (ШО?!), который возвращает родительский метод.
Короче, в данном примере, в методе
❗️Таким образом в дочернем классе мы сузили возвращаемое значение. Не ослабили. А усилили :)
Ставь 🍺, если понял о чем идёт речь или 🐒, если всё это сложно и "давай шото попроще уже".
И сразу продолжение предыдущего поста — "Постусловия не могут быть ослаблены в подклассе".
То есть подклассы должны выполнять все постусловия, которые определены в базовом классе. Постусловия проверяют состояние возвращаемого объекта на выходе из функции. Вот такой пример.
❌Условное выражение проверяющее результат является постусловием в базовом классе, а в наследнике его уже нет. Не делай так!
Сюда-же можно отнести и ковариантность, которая позволяет объявлять в методе дочернего класса типом возвращаемого значения подтип того типа (ШО?!), который возвращает родительский метод.
Короче, в данном примере, в методе
render() дочернего класса, JpgImage объявлен типом возвращаемого значения, который в свою очередь является подтипом Image, который возвращает метод родительского класса Renderer. ❗️Таким образом в дочернем классе мы сузили возвращаемое значение. Не ослабили. А усилили :)
Ставь 🍺, если понял о чем идёт речь или 🐒, если всё это сложно и "давай шото попроще уже".
Канал не быстро, но всё таки растёт ;)
Чтобы я понимал какие темы стоит сюда постить и насколько подробно описывать, проголосуйте плз.
Какой у вас грейд?
Чтобы я понимал какие темы стоит сюда постить и насколько подробно описывать, проголосуйте плз.
Какой у вас грейд?
Anonymous Poll
20%
Trainee / Intern / Junioir 🍺
43%
Middle 🍺🍺
30%
Senior / TL / Arhitect 🍺🍺🍺
7%
Кто здесь? 🐒
Test data builders
В этом посте мы рассматривали паттерн
Как уже было сказано ранее, если тестируемый объект может иметь много разных состояний, то Object Mother сильно раздувается, код внутри методов фабрики будет повторяться, что выглядит не сильно лаконично. Здесь на помощь нам приходит builder. Не используя паттерн у нас получится примерно такой код.
❗️Вроде ничего страшного, но для сравнения код с использованием builder'a.
💡 Код более краток, читабелен и чёток относительно целей теста. Также они носят хороший описательный характер, например если бы мы добавили
Сам подход более гибкий, помогает избежать копипасты, которой в тестах обычно и так хватает. Вы также можете комбинировать оба паттерна (включая
#PHP #testing #junior #source
В этом посте мы рассматривали паттерн
Object Mother. Там я обещал, что расскажу об альтернативном паттерне, который требует больше усилий, но имеет меньше проблем на выходе — Test data builder.Как уже было сказано ранее, если тестируемый объект может иметь много разных состояний, то Object Mother сильно раздувается, код внутри методов фабрики будет повторяться, что выглядит не сильно лаконично. Здесь на помощь нам приходит builder. Не используя паттерн у нас получится примерно такой код.
❗️Вроде ничего страшного, но для сравнения код с использованием builder'a.
💡 Код более краток, читабелен и чёток относительно целей теста. Также они носят хороший описательный характер, например если бы мы добавили
AddressBuilder, то вместо магического числа 21, которое передается в конструктор, написали бы метод ->withHouseNumber(21).Сам подход более гибкий, помогает избежать копипасты, которой в тестах обычно и так хватает. Вы также можете комбинировать оба паттерна (включая
Object Mother) там, где это уместно, таким образом балансируя между гибкостью и количеством затраченного времени.#PHP #testing #junior #source
👍1
Принцип подстановки Барбары Лисков (part 3 — правило истории, инвариантность)
❗️Все условия базового класса - также должны быть сохранены и в подклассе.
Инварианты — это некоторые условия, которые остаются истинными на протяжении всей жизни объекта. Как правило, инварианты передают внутреннее состояние объекта. Например типы свойств базового класса не должны изменяться в дочернем.
❗️Подкласс не должен создавать новых мутаторов свойств базового класса.
Если базовый класс не предусматривал методов для изменения определенных в нем свойств, подтип этого класса так же не должен создавать таких методов. Иными словами, неизменяемые данные базового класса не должны быть изменяемыми в подклассе.
❌ С точки зрения класса
#PHP #middle #source
❗️Все условия базового класса - также должны быть сохранены и в подклассе.
Инварианты — это некоторые условия, которые остаются истинными на протяжении всей жизни объекта. Как правило, инварианты передают внутреннее состояние объекта. Например типы свойств базового класса не должны изменяться в дочернем.
Здесь также стоит упомянуть исторические ограничения («правило истории»):
class Wallet
{
protected float $amount;
// тип данного свойства не должен изменяться в подклассе
}
❗️Подкласс не должен создавать новых мутаторов свойств базового класса.
Если базовый класс не предусматривал методов для изменения определенных в нем свойств, подтип этого класса так же не должен создавать таких методов. Иными словами, неизменяемые данные базового класса не должны быть изменяемыми в подклассе.
❌ С точки зрения класса
Deposit поле не может быть меньше нуля. А вот производный класс VipDeposit, добавляет метод для изменения свойства account, поэтому инвариант класса Deposit нарушается. Такого поведения следует избегать. В таком случае стоит рассмотреть добавление мутатора в базовый класс.#PHP #middle #source
Время, как контекст
Простая и интересная мысль от Matthias Noback:
👉 Рассматривайте время как контекст. Вам следует подумать об определении текущего времени один раз на уровне инфраструктуры (например, в контроллере) и передаче его в качестве значения любым другим объектам, которым нужна эта информация.
А ведь действительно, это поможет сохранить наши функции "чистыми". Поскольку не будет системного вызова для получения даты / времени (в отличие от того, когда мы просто вызываем
#PHP #junior #source
Простая и интересная мысль от Matthias Noback:
👉 Рассматривайте время как контекст. Вам следует подумать об определении текущего времени один раз на уровне инфраструктуры (например, в контроллере) и передаче его в качестве значения любым другим объектам, которым нужна эта информация.
А ведь действительно, это поможет сохранить наши функции "чистыми". Поскольку не будет системного вызова для получения даты / времени (в отличие от того, когда мы просто вызываем
new DateTime ('now') внутри функций), то это позволит нам без проблем писать тесты и mock'aть подобные кейсы.#PHP #junior #source
Matthias Noback
Software Development & Design
Discovering best practices in PHP & Fortran code
Простой способ готовить коллекции
Если вы используете итерируемые объекты (например создаете коллекции) в PHP, то вот маленький лайфхак.
❗️Вместо того, чтобы самостоятельно реализовывать все методы интерфейса
Сложно объяснил, но на примере будет проще ;)
#PHP #junior #advice #source
Если вы используете итерируемые объекты (например создаете коллекции) в PHP, то вот маленький лайфхак.
❗️Вместо того, чтобы самостоятельно реализовывать все методы интерфейса
Iterator (next(), current(), key() и т. д.), вы можете реализовать интерфейс IteratorAggregate с одним единственным методом getIterator(), результатом которого вернуть экземпляр ArrayIterator на основе вашего внутреннего массива.Сложно объяснил, но на примере будет проще ;)
#PHP #junior #advice #source