«phpClub» — архив тем ("тредов"), посвящённых изучению PHP и веб-технологий.
Аноним 2019/05/03 13:50:01  №1393471 1
Насколько плохо делать проверку на instanceof внутри класса-родителя? Типа

[CODE]class A {

function test() { if ($this instanceof B) doSomething(); }

}

class B extends A {}[/CODE]

Просто пришел к тому, что в функции test() класса А надо реализовать функционал, специфичный для класса B, при сохранении всего существующего функционала. Причем эта проверка будет запрятана на глубине одного цикла и try-catch, то есть нельзя выйти из ситуации кодом типа [CODE]class B extends A { function test() { parent::test(); doSomething(); } }.[/CODE]

Как выйти из ситуации, или такое норм?
Ответы: >>1394054 >>1394571
Аноним 2019/05/04 18:12:51  №1394054 2
Аноним 2019/05/05 15:28:30  №1394571 3
>>1394247

Я тоже такой термин не слышал никогда.

>>1394148

Сделай foreach'ем либо через intersect_key.

>>1393471

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

Ты в предке пишешь название класса-наследника. Это неправильно.

Тебе надо правильно проектировать классы, думать о том, какая у каждого зона ответственности. Конкретный совет дать не могу, так как пример у тебя абстрактный. Можно попробовать вынести дублирующийся код в отдельный метод.

>>1393729

Задачи из яндекса легко гуглятся. Мне в общем они нравятся, интересные и есть над чем поломать голову. Я вообще люблю задачи, которые не могу решить сразу.

Ответы: >>1394590
Аноним 2019/05/05 15:50:38  №1394590 4
>>1394571
> Конкретный совет дать не могу, так как пример у тебя абстрактный.
Спасибо за ответ. Всегда приятно читать твои объяснения.

Реальный код такой. Есть класс WebReader, который удобная для меня обертка над file_get_contents, контекстами стримов и $http_response_header.
В нем есть метод doRequest(), внутри которого while и try-catch, и идет запрос через file_get_contents, собственно. В catch обрабатываются исключения, которые кидает file_get_contents. Если исключение такое, что это просто сбой http-запроса - крутим while снова (отправляем новый запрос). Если что-то другое, пробрасываем исключение наружу, конец.
Он не абстрактный, готовый класс для запрос в интернет. Но от него я наследуюсь, если конкретному сайту нужны специфические заголовки, или сохранить-отдать специфические куки. TheSiteWebReader, например. Я все это закладывал при проектировании, поэтому работает замечательно.

И вот пришла беда: на одном из сайтов, на который я лезу через TheSiteWebReader, file_get_contents выдает такую ошибку, которую я могу починить немного починив запрос конкретно для этого сайта, но ни для какого другого. Единственное место, где я могу это починить - глубокие кишки WebReader->doRequest(). И мне нужно проверять, что я TheSiteWebReader, потому что для других сайтов чинить эту ошибку нельзя.

Что делать? Выделить содержимое catch во вспомогательный метод типа WebReader->tryToRepairTheRequest(), а затем при необходимости переопределять этот метод в классах-наследниках? Это будет нормально - передать ему исключение, на основе которого он либо изменит свойства класса (модифицирует запрос), либо кинется переданным исключением, если он не может починить запрос?
Ответы: >>1394733
Аноним 2019/05/06 05:54:58  №1394733 5
>>1394590

> Есть класс WebReader

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

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

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

> Это будет нормально - передать ему исключение, на основе которого он либо изменит свойства класса (модифицирует запрос)

Непонятно, кстати, почему у тебя параметры запроса хранятся в свойствах класса. У тебя объект представляет один запрос и для нового запроса создается новый объект? Если нет, то это может быть ошибкой в архитектуре. Ну например: что, если ты в процессе обработки одного запроса попытаешься через этот класс сделать другой? Свойства второго запроса затрут первый?

>>1394581

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

>>1394582

Несколько try/catch пишется, если ты знаешь, как обработать конкретную ошибку. Иначе пишется один общий обработчик ошибок на все случаи жизни.

>>1388385

В нем ничего не заменено, просто QUERY_STRING - это то, что идет ПОСЛЕ знака вопроса в URL (потому знака вопроса в ней нету) и она вообще может отсутствовать. Открой мануал и проверь: https://www.php.net/manual/ru/reserved.variables.server.php

Также, можешь прочитать урок про структуру URL на всякий случай: https://github.com/codedokode/pasta/blob/master/network/urls.md - там написано, что такое query string

>>1387384

Урок по MVC https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Ответы: >>1394909
Аноним 2019/05/06 19:18:00  №1394909 6
>>1394733
> вынести решение вопроса "что делать при ошибке" в отдельный метод
Вот это мне нравится. Вытащу тогда содержимое catch во вспомогательный метод типа tryToRepairTheRequest().
В родителе будет
protected function tryToRepairTheRequest(\ErrorException $e) {
// можем что-то сделать? если нет, throw $e;
}
В наследниках буду делать
private function tryToRepairTheRequest(\ErrorException $e) {
try { parent::tryToRepairTheRequest($e); }
catch (\ErrorException $e) { // специальный код для наследника, а если не вышло то опять же throw $e; }
}

>У тебя объект представляет один запрос и для нового запроса создается новый объект?
Нет. Запросы могут идти через один объект, а вот ответы - уже новые объекты.
$reader = new WebReader($defaultStreamContext, $maxQtyAttempts); // контекст стрима, который будет использован, если в get() никакой не указан, и кол-во попыток перезапросить при сбоях
$response = $reader->get('https://google.com'[, $streamContext]); // $response - объект класса WebResponse, содержащий ответ
$response2 = $reader->get('https://yandex.ru');

> что, если ты в процессе обработки одного запроса попытаешься через этот класс сделать другой?
Не понял. У меня нет никакой асинхронности и тредов. Вызвал get() - получи WebResponse или эксепшен.
>Свойства второго запроса затрут первый?
Ну, каждый запрос плодит по совершенно независимому WebResponse. WebReader при этом может оставаться один. Все, что он знает - какие хттп-заголовки и параметры стрима выставлять. $url нигде не запоминается и живет только внутри get(), который под капотом вызывает return $this->doRequest($url, $streamContext, $this->qtyAttempts), если ему нравятся переданные параметры. А doRequest() возвращает new WebResponse($http_response_header, $content), куда кладет всё полученное, если запрос удался.
Ответы: >>1398391 >>1402423 >>1402834
Аноним 2019/05/12 13:29:03  №1398391 7
ОПчик, ответь, пожалуйста, нормальная ли архетиктура в >>1394909
Аноним 2019/05/18 10:41:34  №1402423 8
>>1402419
Хочу узнать, нормальное ли у меня ООП в >>1394909
Аноним 2019/05/19 03:51:23  №1402834 9
>>1402686

Вообще, проще оставить все как есть, так как установка делается не так часто. Но если тебе хочется to go deeper ....

В composer на события можно повесить вызов функции или статического метода: https://getcomposer.org/doc/articles/scripts.md#package-events

При этом он получит объект события. Из него как-то можно попробовать выковырять название пакета: https://getcomposer.org/doc/articles/scripts.md#event-classes

Подробности придется искать в коде композера или в Гугле.

Также, хочу заметить, что твои скрипт не кросс-платформенны и работают только под Linux/Mac. Правильнее было бы повесить кроссплатформенный PHP-скрипт.

>>1394909

Ты как-то что-то усложнил. Давай спроектируем все с нуля:

Задача: дать возможность при ошибке поменять реквест и отправить запрос заново. Значит, нужна функция. Она может получать объект ошибки (не ErrorException, который соответствует PHP-ошибке, а например, HttpException) и объект Request. Она может вернуть либо null - не повторять запрос, либо вернуть тот же или модифицированный Request для повтора запроса, либо выбросить какое-то новое исключение:

function handleError(HttpException $e, Request $r): ?Response { ... }

Реализация по умолчанию, естественно, будет возвращать null.

Но тут есть подвох. Что, если мы хотим, например, в ответ на ошибку 503 сделать паузу в 5 секунд и сделать до 3 повторов запроса? Как нам хранить счетчик запросов? В данном случае - хранить его негде (если мы только не предусмотрели это в базовом классе).

Потому можно попробовать другой подход. Делаем переопределяемым сам метод отправки запроса - sendRequest(Request $req): Response. В наследнике просто оборачиваем его:

function sendRequest(...): ...
{
// любой код
parent::sendRequest()
// любой код
}

Это получается более универсальным подходом, как мне кажется.

А еще более универсально будет обернуть выполнение запроса без наследования, обернув вызов метода get().

Также, можно посмотреть на архитектуру питоновской библиотеки urllib, где есть плагины (openers), но она наверно сложновата для твоего случая: https://docs.python.org/3/library/urllib.request.html#module-urllib.request

У тебя в коде ошибки:

- указан слишком общий класс для отлова исключений
- нет реализации по умолчанию

Вообще, подход с оборачиванием проще, чем с обработчиком ошибок, как мне кажется.
Ответы: >>1402868
Аноним 2019/05/19 07:21:38  №1402868 10
>>1402834
> скрипт не кросс-платформенны и работают только под Linux/Mac.
Я не автор того скрипта, но не понял зачем давать такой совет. Под виндой бекенд обычно не разрабатывают, во многих компаниях наоборот распространена практика пересаживания всех на мак/убунту, чтобы проекты можно было быстрее поднимать по инструкции. Никогда не видел, чтобы в проекте было несколько параллельных инструкций для разных ОС. Зато часто видел древние проекты, которые не то что на винде запустить нельзя, их нужно запускать только под определённой версией PHP и только под апачем, так как на его rewrite правилах и includ'ах завязан весь код.
Ответы: >>1402920 >>1405044
Аноним 2019/05/19 09:35:10  №1402920 11
>>1402868
> бекенд обычно не разрабатывают
Net Core
Аноним 2019/05/22 23:52:06  №1405044 12
>>1403996

Ты ищешь сложное решение вместо простого. В случае использования элемента display: block и задания ему ширины, маргины вычисляются автоматически. Достаточно задать левый маргин, ширину и поставить auto для правого маргина.

Как плюс, это работает вообще во всех браузерах, в отличие от calc(), который пришел только с CSS3 и внедрен с ~2013 года: https://caniuse.com/#feat=calc

Обрати также внимание, что в части старых бразеров calc доступен только с вендорным префиксом. Изучи эту тему, и добавь нужные префиксы, если ты хочешь использовать calc(). Также, добавь в закладки или запомни сайт caniuse, он наверняка еще пригодится.

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

>>1403678

Вообще, я бы советовал просто прочитать цикл статей "создаем приложение на фреймворке X". Вот, например, например, первый попавшийся туториал по Symfony: https://auth0.com/blog/symfony-tutorial-building-a-blog-part-1/

А так, пожалуйста, сложное облачное хранилище файлов nextcloud: https://github.com/nextcloud/server

CMS Интернет-магазина Magento: https://github.com/magento/magento2

>>1402868

Я довольно долгое время под виндой работал, PHP и Апач там вполне работают.

На винду поставить несколько версий PHP проще простого - скачиваем zip-архивы и распаковываем в разные папки. А под линукс? Либо заморачиваться со сторонними репозиториями, которые перезапишут тебе libssl, либо использовать работающие с переменныем успехом скрипты вроде phpenv.