я не говорю что HMAC или все симетричное шифрование это плохо и не правильно, я говорю что в приведенной в статье примере с сервисом авторизации и application, использовать один секрет не стоит, особенно если существуют и другие сервисы которые доверяют сервису авторизации, потому что это не безопасно не с точки зрения jwt, а скорее здравого смысла.
если несколько серверов или отдельных сервисов и использовать симметричное шифрование и раздавать этот ключ всем сервисам, то при компрометации одного из них, ключ станет известен злоумышленнику и он сможет выписать любые токены.
Для ситуации когда несколько сервисов, удобней использовать ассимитричное шифрование, когда есть одно место которое выдает токены и только оно знает приватный ключ, остальные сервисы используют публичный ключ для проверки подписи и не знаю приватный ключ, компрометация одного из сервисов не приводит к возможности генерировать произвольные токены злоумышленника, которые могут помочь скомпрометировать и другие сервисы.
в примере в статье речь о симметричном шифрование и секрет получается один и тот же что на auth сервисе, что на application.
еще раз по порядку.
в случае если у вас несколько сервисов и один и тот же ключ используется во всех сервисах и доступен он между сервисами, если злоумышленник получит доступ к одному из сервисов, он сможет получить доступ к ключу, а с помощью ключа он сможет генерировать какие угодно токены, чем больше сервисов знает общий секрет, тем выше шансы на компрометацию.
В такой ситуации безопасней чтобы сервисы для валидации токена обращались к сервису авторизации и он уже проверял валидный он или нет, тем самым получив доступ к другим сервисам, они не смогут выпускать произвольные токены, а более правильно это ассимитричное шифрование, как человек тоже написал ниже, что есть приватный ключ и он известен в одном месте(в котором выдают токены), остальные сервисы знают только ПУБЛИЧНЫЙ ключ, с помощью которого могу проверять подписи, это публичный ключ можно хоть всему миру рассказать, главное что для генерация токена надо приватный ключ.
давай те по порядку.
В статье говориться что секрет это то с помощью чего подписывается, далее в статье упоминается что секрет знают и сервер авторизации и сервер приложений, я лишь подчеркнул что это не правильно
Если подпись идет с помощью rsa то там ПУБЛИЧНЫЙ (не секретный) ключ и его может знать вообще кто угодно, а приватный ключ у сервера авторизации останется, как описали в коменте ниже.
При этом больше замечаний к статье не имею и в целом она очень адекватно написана, я лишь указал на то что неправильно трактовать можно прочитав статью
вы не правильно пишите что application server должен знать секрет, какой же это секрет если о нем знает несколько сервисов
Смысл в том что application server может читать данные из payload без ключа, а когда надо их проверить (verify) то он может взять этот же токен и отправить его на сервер авторизации, а он уже зная секрет сообщит валидный это токен или нет.
спасибо радует второй абзац что можно остаться на старых тарифах.
У йоты старые тарифы одни из лучших(не сочтите за рекламу но в моем регионе это дейтсвительно так), а вот новые тарифы лично мне очень не нравятся и переходить на них я не буду.
По поводу качества связи, внутри помещений в некоторых ситуациях связь пропадает(опускается до 2g например и тд), я прекрасно понимаю с чем это связано и прекрасно понимаю что из за одного обращения не будут делать апгрейд оборудования, а программные способы известны и их потенциал крайне ограничен, а плохая связь и так наверно мониторится и там где нагружено оборудование.
нет меня еще не перевели, но начал искать места куда идти в случае чего.
Потому что я могу понять плохую сотовую связь или плохое покрытие интернет особенно 3G (везде перегруженно) хорошо хоть 4G не сильно загружено, но за такое платить столько денег, это без меня.
что за булшит, пользователи жаловались что тарифы слишком безлимитны, сделайте нам потолок в 30гб? сомневаюсь, больше похоже на попытку состряпать хорошую мину при плохой игре.
Давайте по порядку, я пользователю yota и при моих текущий тратах в 250 руб примерно, я получал безлимитный интернет (пусть с ограничениеми в виде сайтов не для мобилок или ограничениями других типах трафика не будем об этом), я получал 300 минут без лимита по России и на звонки на другие операторы, даже не смотря на откровенно ужасную связь местами, в некоторых местах 3g перегружен на столько что пользоваться интернетом просто невозможно и тд.
Что теперь я могу получить за эти самые 250 руб по новым тарифам? после принудутильного перехода на новые тарифы, мне станет лучше как клиенту? сомневаюсь.
В моей ситуации придется поступить просто, голосовать ногами, ну а йоте удачи.
docker прикольная вещь и она довольно быстро стартует, по сравнению со связкой vagrant + virtualbox только там всякие забавы идут с windows и mac и есть подход в котором докер крутится внутри виртуалки.
Поэтому все же проще vagrant + virtualbox
У него есть минусы:
Потребляет лишнюю память и cpu (в среднем 1gb ram и проц)
Разворачивается не быстро (в среднем 1 минута).
На больших проектах синхронизация файлов по умолчанию тормозит (обычно используют rsync или nfs что выливается в пляски с windows хорошо если лицензия корпоративная или проффесиональная)
Но есть и огромное количество плюсов
по поводу provision, не обязательно использовать что то типо chef, ansible или puppet, можно просто использовать bash script в простых случаях как вы описали выше.
Вообщем если есть на это спрос могу написать конечно статью на хабр на эту тему как я разворачиваю окружение ну и конфиг соответственный.
открыл хабр получил статью по нетворкингу (любая книга это содержит, просто чуть от фильтровали для интровертов и место указали как техническая конференция).
Валится значит выполнение программы дальше бесмысленно, она останавливает обработку запроса корректно сообщая о том что завершена с ошибкой.
Далее вы предлагаете вести разработку в стиле "Exception Drive Development (EDD)" и кидаться ими по стеку и заниматься рыбалкой (вылавливанием) я конечно понимаю что она имеет право на жизнь как например "GOTO Drive Development"(GTD) она даже может быть чуть похожа, но заниматься этим я этим все таки не буду, при этом я прекрасно знаю о важности обработки исключений и о том что есть RuntimeException и что в ходе обработки исключений программа может вернутся к нормальному исполнению (обработка с возвратом).
EDD и GDD это естественно выдуманные слова.
Для передачи сообщений вверх по стеку я предпочитаю использовать древнюю магию return и код который старается однозначна себя вести и использовать исключения по назначению, а не вместо return.
Я на своем подходе не настаиваю я просто пояснил как бы я и некоторые другие люди могут воспринимать валидацию и если моя позиция не сходится с вашей здесь нет ничего страшного.
Теперь по поводу кто какой программист это наверно не вам решать, о том навыках использования исключениями тоже, но как и все могу сказать что я далеко не идеальный и всегда бывают баги и если вы в своей жизни не допустили не одной баги в разработке, то либо очень мало разработали, либо еще не знаете о этих багах, ну или не считаете их за баги.
Я же все таки советовал бы почитать http://bfy.tw/BzlD и что исключения это конечно инструмент разработчика, но наверно все таки не ведения разработки через исключения, потому что при большом проекте и таким гуляниям по стеку становится мучительно больно это поддерживать и чем больше проект тем больше это напоминает goto разработку.
и дальнейшее обсуждение возможно будет не очень конструктивным, давайте каждый останется при своем мнение, потому то как использовать исключение это очень спорная тема и существуют разные подходы, о которых я знаю, в том числе и о том вы говорите, но я хочу сказать что есть и другие и иногда они бывают лучше а иногда хуже.
Валидация подразумевает два состояния, валидно и не валидно и если не валидно должен быть функционал получения текста что именно не валидно, это нужно для того чтобы выбросить соответствующий тип исключения с соответствующим текстом в том месте где вы вызывали валидацию, также вместо исключения может быть предусмотренно поведение в случае невалидных данных
Например у вас спрашивают год рождения вы указываете 2222 с точки зрения валидации это не правильно год рождения не может быть больше текущего года и прекращать и падать всему приложению нет нужды, потому что после вашего кода может быть например логирование в приложение которое залогировало входящий запрос, а выходящий (ответ) не залогировало, вы можете сказать так сделайте логирование в обработке exception но это не правильно вы начинаете делать ветвление логики и писать ее в обработку исключения потом может появится еще что то.
Вторая причина почему валидация не должна падать в корку это когда в форме спрашивают дату рождения и вы указываете 34.34.2222 тут сразу несколько ошибок валидации, неправильный номер месяца так как он больше 12, не правильный день и не правильный год, в случае валидации которая падает с exception она будет каждый этот exception выкидывать по одному, вы заметите что это должно проверятся на клиенте(js) и к вам уже приходить валидно и backend не обязан давать человеко читаемый текст, поэтому мы можем ронять приложение, но ронять резко приложение плохо смотри пункт один что могут быть события в конце обработки запроса.
В противовес слову validation есть assert который как раз утверждение и если оно не истинно оно валится с исключением.
tldr Я как потенциальный пользователь этого трейта видя validation ожидаю там валидацию, а не assertation, поэтому вам нужно или сделать там валидацию или переназвать метод на assertation, второе как мне кажется более правильно и менее трудо затратно, просто я имею ввиду что вы выбрали плохое имя для метода.
tldr Давайте валидировать входные данные ручками все правила валидации сложим в трейт который будем подключать.
Реальность такова что "строгую" типизацию ввели как раз из за того что люди очень много валидировали параметры ручками и это вынесли на уровень языка, значит это уже делали до вас и ничего нового тому кому нужна статическая типизация вы не сказали.
Далее trait далеко не лучшая идея для хранения этих проверок, можно с тем же успехом сделать Util\Assert и кучу статических методов которые дерграть, это как пример, или сложить в функцию которую импортить.
Следующее аргумент функции валидации это массив где магическое имя ключа и значение которое он должен проверить, на самом деле лучше сделать что то типо assertString($arg...), assertInt($arg...), assertNotNull($arg ...)
Следующее ваш способ валидации предполагает выкидывания исключений, это сделано для того чтобы в коде меньше проверять ответы и в случае чего падать в корку, но это далеко не лучшая практика потому что ошибка в данных это нормальная ситуация и она должна быть обработано и по возможности без исключений, поэтому имя в виде валидации не очень здесь подходит.
tdlr С учетом всего выше сказанного вы просто написали еще одну библиотеку содержащую assert которых и так было достаточно и которые учитывают все мои замечания указанные выше.
Ну а теперь немножко позитива то что вы думаете над такими проблемами это хорошо и попытка сделать инструмент который можно было бы переиспользовать это тоже хорошо и попытка им поделится, вы как минимум получите замечания (рекомендации) по его улучшению и работая над его улучшением будете улучшать свои навыки и это лично для вас будет плюсом ну и попутно для других.
А сейчас посмотрите https://packagist.org/search/?q=assert количество библиотек и по смотрите как они решают подобную проблему может что полезное для себя узнаете.
Кстате еще один интересный подход http://php.net/manual/ru/book.stream.php можно с помощью эту функциональности сделать что то типо преобразования одного кода в другой и установить эти проверки на этапе "компиляции" и тогда ручные проверки делать не обязательно, можно например сразу код писать в стиле php 7 но для запуска его в php5 надо будет его преобразовать, выглядит забавно чисто с точки зрения иследования, на продакшене я бы такое применять не стал
p.s. работал в крупных компаниях банке например и там везде фиксированная ЗП которая берется из ФОТ (фонд оплаты труда) и в договоре тоже была указана точная сумма
а чем лучше будет с queryBuilder-oм? тоже самое придется написать
Единственный плюс с queryBuilder-ом порядок условий не важен, но это тоже реализуемо, без нового заморского синтаксиса.
Проблема всех query билдеров в том что надо учить новый синтаксис, вопрос зачем? я вначале представляю какой SQL запрос надо сделать, потом пытаюсь этот SQL преобразовать с помощью доки к библиотеке, зачем мне делать последний шаг и тратить время
Простейшая обертка вроде:
$user = $db->selectOneSQL('SELECT * FROM users WHERE id = :id', ['id' => 123]);
var_dump($user); // структура в виде массива
$users = $db->selectSQL('SELECT * FROM users'); // возвращаем statment с итератором
foreach ($users as $user) {
var_dump($user);
}
$user = $db->selectOne('users', ['id' => 123]); // тот же самый результат что и первый запрос selectOneSQL
$db->select('users'); // Тот же самый результат что у второго запроса selectSQL
$db->query('UPDATE users SET email = :newValue WHERE id = :id', [
'newValue' => 'test@mail.ru'
], [
'id' => 123
]
);
// простейшая обертка над update делает тоже самое
$db->update('users', ['email' => 'test@mail.ru'], ['id' => 123]);
Чем такой подход хуже ?
только я могу копировать запросы и отлаживать их при желание или делать explain или другие вещи ?
При этом я знаю о ORM и даже иногда использую их только чем ваша библиотека лучше doctrine/dbal ?
я не говорю что HMAC или все симетричное шифрование это плохо и не правильно, я говорю что в приведенной в статье примере с сервисом авторизации и application, использовать один секрет не стоит, особенно если существуют и другие сервисы которые доверяют сервису авторизации, потому что это не безопасно не с точки зрения jwt, а скорее здравого смысла.
если несколько серверов или отдельных сервисов и использовать симметричное шифрование и раздавать этот ключ всем сервисам, то при компрометации одного из них, ключ станет известен злоумышленнику и он сможет выписать любые токены.
Для ситуации когда несколько сервисов, удобней использовать ассимитричное шифрование, когда есть одно место которое выдает токены и только оно знает приватный ключ, остальные сервисы используют публичный ключ для проверки подписи и не знаю приватный ключ, компрометация одного из сервисов не приводит к возможности генерировать произвольные токены злоумышленника, которые могут помочь скомпрометировать и другие сервисы.
в примере в статье речь о симметричном шифрование и секрет получается один и тот же что на auth сервисе, что на application.
еще раз по порядку.
в случае если у вас несколько сервисов и один и тот же ключ используется во всех сервисах и доступен он между сервисами, если злоумышленник получит доступ к одному из сервисов, он сможет получить доступ к ключу, а с помощью ключа он сможет генерировать какие угодно токены, чем больше сервисов знает общий секрет, тем выше шансы на компрометацию.
В такой ситуации безопасней чтобы сервисы для валидации токена обращались к сервису авторизации и он уже проверял валидный он или нет, тем самым получив доступ к другим сервисам, они не смогут выпускать произвольные токены, а более правильно это ассимитричное шифрование, как человек тоже написал ниже, что есть приватный ключ и он известен в одном месте(в котором выдают токены), остальные сервисы знают только ПУБЛИЧНЫЙ ключ, с помощью которого могу проверять подписи, это публичный ключ можно хоть всему миру рассказать, главное что для генерация токена надо приватный ключ.
давай те по порядку.
В статье говориться что секрет это то с помощью чего подписывается, далее в статье упоминается что секрет знают и сервер авторизации и сервер приложений, я лишь подчеркнул что это не правильно
Если подпись идет с помощью rsa то там ПУБЛИЧНЫЙ (не секретный) ключ и его может знать вообще кто угодно, а приватный ключ у сервера авторизации останется, как описали в коменте ниже.
При этом больше замечаний к статье не имею и в целом она очень адекватно написана, я лишь указал на то что неправильно трактовать можно прочитав статью
есть 3 вида лжи: ложь, наглая ложь и оценка трудозатрат
вы не правильно пишите что application server должен знать секрет, какой же это секрет если о нем знает несколько сервисов
Смысл в том что application server может читать данные из payload без ключа, а когда надо их проверить (verify) то он может взять этот же токен и отправить его на сервер авторизации, а он уже зная секрет сообщит валидный это токен или нет.
по поводу второго пункта это в любой бд это же проверка целостности и ее никак не сделать по другому.
спасибо радует второй абзац что можно остаться на старых тарифах.
У йоты старые тарифы одни из лучших(не сочтите за рекламу но в моем регионе это дейтсвительно так), а вот новые тарифы лично мне очень не нравятся и переходить на них я не буду.
По поводу качества связи, внутри помещений в некоторых ситуациях связь пропадает(опускается до 2g например и тд), я прекрасно понимаю с чем это связано и прекрасно понимаю что из за одного обращения не будут делать апгрейд оборудования, а программные способы известны и их потенциал крайне ограничен, а плохая связь и так наверно мониторится и там где нагружено оборудование.
а на какой скорости?
здесь маркетинговые уловки к счастью люди понимают
нет меня еще не перевели, но начал искать места куда идти в случае чего.
Потому что я могу понять плохую сотовую связь или плохое покрытие интернет особенно 3G (везде перегруженно) хорошо хоть 4G не сильно загружено, но за такое платить столько денег, это без меня.
что за булшит, пользователи жаловались что тарифы слишком безлимитны, сделайте нам потолок в 30гб? сомневаюсь, больше похоже на попытку состряпать хорошую мину при плохой игре.
Давайте по порядку, я пользователю yota и при моих текущий тратах в 250 руб примерно, я получал безлимитный интернет (пусть с ограничениеми в виде сайтов не для мобилок или ограничениями других типах трафика не будем об этом), я получал 300 минут без лимита по России и на звонки на другие операторы, даже не смотря на откровенно ужасную связь местами, в некоторых местах 3g перегружен на столько что пользоваться интернетом просто невозможно и тд.
Что теперь я могу получить за эти самые 250 руб по новым тарифам? после принудутильного перехода на новые тарифы, мне станет лучше как клиенту? сомневаюсь.
В моей ситуации придется поступить просто, голосовать ногами, ну а йоте удачи.
дело в том что я не devops а обычный разработчик, просто люблю комфортную работу и хорошо умею настрайвать всякие штуки.
docker прикольная вещь и она довольно быстро стартует, по сравнению со связкой vagrant + virtualbox только там всякие забавы идут с windows и mac и есть подход в котором докер крутится внутри виртуалки.
Поэтому все же проще vagrant + virtualbox
У него есть минусы:
Но есть и огромное количество плюсов
по поводу provision, не обязательно использовать что то типо chef, ansible или puppet, можно просто использовать bash script в простых случаях как вы описали выше.
Вообщем если есть на это спрос могу написать конечно статью на хабр на эту тему как я разворачиваю окружение ну и конфиг соответственный.
используйте vagrant например для подобных историй.
настройку машины можно сложить в provision
открыл хабр получил статью по нетворкингу (любая книга это содержит, просто чуть от фильтровали для интровертов и место указали как техническая конференция).
давайте сразу проясним
Валится значит выполнение программы дальше бесмысленно, она останавливает обработку запроса корректно сообщая о том что завершена с ошибкой.
Далее вы предлагаете вести разработку в стиле "Exception Drive Development (EDD)" и кидаться ими по стеку и заниматься рыбалкой (вылавливанием) я конечно понимаю что она имеет право на жизнь как например "GOTO Drive Development"(GTD) она даже может быть чуть похожа, но заниматься этим я этим все таки не буду, при этом я прекрасно знаю о важности обработки исключений и о том что есть RuntimeException и что в ходе обработки исключений программа может вернутся к нормальному исполнению (обработка с возвратом).
EDD и GDD это естественно выдуманные слова.
Для передачи сообщений вверх по стеку я предпочитаю использовать древнюю магию return и код который старается однозначна себя вести и использовать исключения по назначению, а не вместо return.
Я на своем подходе не настаиваю я просто пояснил как бы я и некоторые другие люди могут воспринимать валидацию и если моя позиция не сходится с вашей здесь нет ничего страшного.
Теперь по поводу кто какой программист это наверно не вам решать, о том навыках использования исключениями тоже, но как и все могу сказать что я далеко не идеальный и всегда бывают баги и если вы в своей жизни не допустили не одной баги в разработке, то либо очень мало разработали, либо еще не знаете о этих багах, ну или не считаете их за баги.
Я же все таки советовал бы почитать http://bfy.tw/BzlD и что исключения это конечно инструмент разработчика, но наверно все таки не ведения разработки через исключения, потому что при большом проекте и таким гуляниям по стеку становится мучительно больно это поддерживать и чем больше проект тем больше это напоминает goto разработку.
и дальнейшее обсуждение возможно будет не очень конструктивным, давайте каждый останется при своем мнение, потому то как использовать исключение это очень спорная тема и существуют разные подходы, о которых я знаю, в том числе и о том вы говорите, но я хочу сказать что есть и другие и иногда они бывают лучше а иногда хуже.
давайте я немного поясню
Валидация подразумевает два состояния, валидно и не валидно и если не валидно должен быть функционал получения текста что именно не валидно, это нужно для того чтобы выбросить соответствующий тип исключения с соответствующим текстом в том месте где вы вызывали валидацию, также вместо исключения может быть предусмотренно поведение в случае невалидных данных
Например у вас спрашивают год рождения вы указываете 2222 с точки зрения валидации это не правильно год рождения не может быть больше текущего года и прекращать и падать всему приложению нет нужды, потому что после вашего кода может быть например логирование в приложение которое залогировало входящий запрос, а выходящий (ответ) не залогировало, вы можете сказать так сделайте логирование в обработке exception но это не правильно вы начинаете делать ветвление логики и писать ее в обработку исключения потом может появится еще что то.
Вторая причина почему валидация не должна падать в корку это когда в форме спрашивают дату рождения и вы указываете 34.34.2222 тут сразу несколько ошибок валидации, неправильный номер месяца так как он больше 12, не правильный день и не правильный год, в случае валидации которая падает с exception она будет каждый этот exception выкидывать по одному, вы заметите что это должно проверятся на клиенте(js) и к вам уже приходить валидно и backend не обязан давать человеко читаемый текст, поэтому мы можем ронять приложение, но ронять резко приложение плохо смотри пункт один что могут быть события в конце обработки запроса.
В противовес слову validation есть assert который как раз утверждение и если оно не истинно оно валится с исключением.
tldr Я как потенциальный пользователь этого трейта видя validation ожидаю там валидацию, а не assertation, поэтому вам нужно или сделать там валидацию или переназвать метод на assertation, второе как мне кажется более правильно и менее трудо затратно, просто я имею ввиду что вы выбрали плохое имя для метода.
Надеюсь я чуть прояснил ситуацию
tldr Давайте валидировать входные данные ручками все правила валидации сложим в трейт который будем подключать.
Реальность такова что "строгую" типизацию ввели как раз из за того что люди очень много валидировали параметры ручками и это вынесли на уровень языка, значит это уже делали до вас и ничего нового тому кому нужна статическая типизация вы не сказали.
Далее trait далеко не лучшая идея для хранения этих проверок, можно с тем же успехом сделать Util\Assert и кучу статических методов которые дерграть, это как пример, или сложить в функцию которую импортить.
Следующее аргумент функции валидации это массив где магическое имя ключа и значение которое он должен проверить, на самом деле лучше сделать что то типо assertString($arg...), assertInt($arg...), assertNotNull($arg ...)
Следующее ваш способ валидации предполагает выкидывания исключений, это сделано для того чтобы в коде меньше проверять ответы и в случае чего падать в корку, но это далеко не лучшая практика потому что ошибка в данных это нормальная ситуация и она должна быть обработано и по возможности без исключений, поэтому имя в виде валидации не очень здесь подходит.
tdlr С учетом всего выше сказанного вы просто написали еще одну библиотеку содержащую assert которых и так было достаточно и которые учитывают все мои замечания указанные выше.
Ну а теперь немножко позитива то что вы думаете над такими проблемами это хорошо и попытка сделать инструмент который можно было бы переиспользовать это тоже хорошо и попытка им поделится, вы как минимум получите замечания (рекомендации) по его улучшению и работая над его улучшением будете улучшать свои навыки и это лично для вас будет плюсом ну и попутно для других.
А сейчас посмотрите https://packagist.org/search/?q=assert количество библиотек и по смотрите как они решают подобную проблему может что полезное для себя узнаете.
Кстате еще один интересный подход http://php.net/manual/ru/book.stream.php можно с помощью эту функциональности сделать что то типо преобразования одного кода в другой и установить эти проверки на этапе "компиляции" и тогда ручные проверки делать не обязательно, можно например сразу код писать в стиле php 7 но для запуска его в php5 надо будет его преобразовать, выглядит забавно чисто с точки зрения иследования, на продакшене я бы такое применять не стал
ну значит я не в большинстве компаний
p.s. работал в крупных компаниях банке например и там везде фиксированная ЗП которая берется из ФОТ (фонд оплаты труда) и в договоре тоже была указана точная сумма
а чем лучше будет с queryBuilder-oм? тоже самое придется написать
Единственный плюс с queryBuilder-ом порядок условий не важен, но это тоже реализуемо, без нового заморского синтаксиса.
Проблема всех query билдеров в том что надо учить новый синтаксис, вопрос зачем? я вначале представляю какой SQL запрос надо сделать, потом пытаюсь этот SQL преобразовать с помощью доки к библиотеке, зачем мне делать последний шаг и тратить время
Простейшая обертка вроде:
Чем такой подход хуже ?
только я могу копировать запросы и отлаживать их при желание или делать explain или другие вещи ?
При этом я знаю о ORM и даже иногда использую их только чем ваша библиотека лучше doctrine/dbal ?