Функции и параметры, как правильно...

Yaponchick

Новичок
Здравствуйте ;)

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

Пример:

PHP:
function get_items($items) {
  $ret = array();
  
  $ret = $this->db->fetchAll("... very big sql query ...");
  
  return $ret;
}
Ситуация №1: Пользователь дал мне список названий "товаров", т.е. array("Вазелин". "Фейри", "Мыло на посошок");
Ситуация №2: Пользователь дал мне ID товаров, array(45, 35, 44);
Ситуация №3: Пользователь дал смесь ID & Names, содержание массива, и так ясно будет )

Если сделать унифицированную функцию, которая ищет как по названиям так и по ID, то при возвращение массива, возникает вопрос ключей. Если ключ будет равен элементу входящего массива, то это самый убогий вариант. Но зато просто проверить существование, e.g.
PHP:
isset(get_items(["a", "b", 1, 45])["b"]);

Если ключ будет равен ID товара, то для проверки "существования" товара придётся перебирать весь массив, e.g.
PHP:
foreach (get_items(["a", "b", 1, 45]) as $item) { if ($item["name"] == "b") { print "got it"; } }

А если ограничить входного массива до IDs, то понадобится 2ая функция, в случае, если от пользователя пришли названия -.-

Так что, может где-то написано, как правильно создавать функции?
 

fixxxer

К.О.
Партнер клуба
Ситуация №1: Пользователь дал мне список названий "товаров", т.е. array("Вазелин". "Фейри", "Мыло на посошок");
$GoodsModel->findByNames(array("Вазелин". "Фейри", "Мыло на посошок"))
Ситуация №2: Пользователь дал мне ID товаров, array(45, 35, 44);
$GoodsModel->findByIds(array(45, 35, 44))
Пользователь дал смесь ID & Names
throw new InvalidArgumentException
 

Yaponchick

Новичок
А если смысл как раз в двойном чтении и того и того? Допустим приходиться анализировать "кривой" лог, в котором в разных случаях ID || Name.
Тогда самому разбивать вручную, на names vs ids arrays?
 

fixxxer

К.О.
Партнер клуба
Так заранее ты все равно не знаешь, в каком месте пересечения.

Анализ кривого лога - это точно не тот кейс, под который строится api - если уж действительно надо, можно отдельный метод сделать, который разобьет на id и names и сделает запрос id in ... or name in ...
 

Yaponchick

Новичок
А можно сразу параллельный вопрос? Функция должна получать заведомо правильные данные или ОНА должна проверять правильность?
Пример: добавляем аватар пользователю, проверять наличие пользователя до вызова insert_avatar($userid, $image_path); или уже в ней? )
 

alekciy

Новичок
проверять наличие пользователя до вызова insert_avatar($userid, $image_path); или уже в ней? )
До. Метод должен выполнять возложенную на него функцию и не более. Возложенная функция должна быть отражена в имени. Это позволяет делать код метода проще, а движок в целом гибче.

В указанном примере так же стоит опирать на механизмы контроля целостности используемой СУБД, т.к. между моментом проверки и вставкой аватара пользователя могли удалить.
 

korpus

злой бобёр
До. Метод должен выполнять возложенную на него функцию и не более. Возложенная функция должна быть отражена в имени. Это позволяет делать код метода проще, а движок в целом гибче.

В указанном примере так же стоит опирать на механизмы контроля целостности используемой СУБД, т.к. между моментом проверки и вставкой аватара пользователя могли удалить.
А что на счёт незначительной проверки правильности параметров, передаваемых в функцию? Допустим, в функцию надо передать целое число, и только целое число сможет обеспечить то, что код будет выполнен без ошибок. Ошибка может быть на уровне PHP (может выскочить предупреждение), так и логическая ошибка, приводящая к неправильному результату. Проверка параметра и его приведение к нужному виду может потребовать нескольких дополнительных строчек кода.
PHP:
$var = (int) $var;
В примере самый простейший случай, который может усложниться, если необходимо, например, чтобы число было неотрицательным. Как в этом случае поступать? Тоже всегда волновал этот вопрос.
Есть два варианта решения:
а) всю ответственность возложить на того, кто будет пользоваться функцией, чтобы он делал проверку данных самостоятельно
б) проверять данные в начал функции
 

С.

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

fixxxer

К.О.
Партнер клуба
Важно понимать разделение ответственности: что волнует функцию, а что вызывающий код.

Здесь критерии слабо определены, потому возьмем иной, очевидный пример с составлением sql-запросов - очевидно, что экранировать (mysql+real_escape_string etc) должна сама функция выполнения запроса, а уж насколько уместны передаваемые денные - это уже заюота модели, а не sql builder-а.
 

alekciy

Новичок
А что на счёт незначительной проверки правильности параметров, передаваемых в функцию? Допустим, в функцию надо передать целое число, и только целое число сможет обеспечить то, что код будет выполнен без ошибок.
Странное желание для PHP. Языка без строгой типизации. Не бывает значительных или незначительных проверок. В функции/методе выполняющей определенную логику должна быть только это логики и не более. В противном случае можно очень быстро замусорить основной код проверками которые сначала могут быть незначительны, а потом дополняться, дополняться, дополняться... пока за ними не будет видно уже основного кода. Прямой путь к быдлокодигу. Нужны данные определенного формата, ну так и нужно их перед передачей прогнать через валидатор/нормализатор/другое. А если уж данные таки дошли в недопустимой форме, то по мне пусть все и валится. Мне подход erlang-а "let it crash" очень нравится. Пусть дохнет и зафиксирует это в логе, а разработчик потом разбирается, почему возникла такая ситуация. Не должен софт замалчивать о проблеме. Тем более наверняка на текущий код завязано еще куча другой логики которая не сможет работать корректно если в текущем коде все сдохло. Так что ни каких проверок, путь дохнет если что как можно раньше и активно кричит об этом.

В примере самый простейший случай, который может усложниться, если необходимо, например, чтобы число было неотрицательным. Как в этом случае поступать? Тоже всегда волновал этот вопрос.
Есть два варианта решения:
а) всю ответственность возложить на того, кто будет пользоваться функцией, чтобы он делал проверку данных самостоятельно
б) проверять данные в начал функции
Поддержания API возлагаем на клиента использующего наше API. Если он передал не корректные данные и это привело к падению кода, так тому и быть.

Все выше приведенное вовсе не претендует на соблюдение всегда. Но если нет объективных причин нарушить данный подход, то нарушать его не следует.
 

itprog

Cruftsman
А если уж данные таки дошли в недопустимой форме, то по мне пусть все и валится.
crash в случае ошибки - это лучший кейс. В худшем - со счета клиента исчезло несколько миллионов.
 

korpus

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

korpus

злой бобёр
Странное желание для PHP. Языка без строгой типизации..
А может нельзя в функцию ноль передавать, что тогда? Это проверка не типа переменной, а уже более сложная проверка. Если передать ноль, то вылезет ошибка. И этот ноль может вылезти в случае, если данных приходят из браузера пользователя. Об этом тогда можно позаботиться или внутри функции или в скрипте, использующем функцию. Проблема в том, что тот, кто пишет скрипт может не знать всех ньюансов работы используемой функции. А значит ошибка возникнет не по вине разработчиков, а по вине пользователя браузера.
 

alekciy

Новичок
А может нельзя в функцию ноль передавать, что тогда? Это проверка не типа переменной, а уже более сложная проверка. Если передать ноль, то вылезет ошибка. И этот ноль может вылезти в случае, если данных приходят из браузера пользователя. Об этом тогда можно позаботиться или внутри функции или в скрипте, использующем функцию. Проблема в том, что тот, кто пишет скрипт может не знать всех ньюансов работы используемой функции. А значит ошибка возникнет не по вине разработчиков, а по вине пользователя браузера.
Нельзя передавать и передали, значит скрипт должен валиться с ошибкой и дохнуть. А дальше уже разбираться, почему такая ситуация вообще возникла. Данные приходящие из браузера должны фильтроваться и нормализоваться в специально созданном для этого классе валидатора. Именно в нем будет сосредоточена бизнес логика задача которой как раз и заключается в разного рода проверках. А код отрабатывающий другую бизнес логику должен именно её и реализовывать и не заниматься ни какими проверками. Мухи отдельно, котлеты отдельно. Не нужно размазывать логику проверок по всему движку. Тогда на супорте такого кода мы не получим ситуацию "ааа, в какой же жопе сосредоточена проверка для этого куска кода?!". И это не говоря еще про дублирование кода.

Кто пишет скрипт может и не знать всех ньансов работы функции. Более того, он их знать и не должен! Инкапсуляция, да. А вот API он знать и следовать ему обязан. Не следует, ну так die и пламенные привет в логах. В общем "картины рисовать" (с) Д. Потапенко.
 

alekciy

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

korpus

злой бобёр
В одних ситуациях надо делать проверку в функции, в других случаях оставлять всё на долю скриптов. Тут готового решения не существует.
 
Сверху