Боятся ли PHP-разработчики функций?

Original author: Nikita Popov
  • Translation
Недавно я обратил внимание на одну вещь, которая стала меня беспокоить: PHP-программисты не используют функции.

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

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

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

И это меня в некотором смысле беспокоит, я считаю неправильным распихивать все по классам. Такой подход ведет к Классо-Ориентированному Программированию, когда использование классов становится самоцелью. (Напомню: наличие классов само по себе не делает код объектно-ориентированным!)

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

function array_rand_value(array $array) {
    if (empty($array)) return null;
    return $array[array_rand($array)];
}

Малюсенькая функция, ничего такого. Но тут возникает самая главная по моему мнению сложность: где ее расположить? И как подружить ее с автозагрузкой?

PHP во многом перенял ООП-модель из Явы, в частности однозначное соответствие: один класс — один файл. Сам язык этого не требует, однако это уже общепринятое соглашение. Более того, оно поддерживается механизмом автозагрузки и стандартом PSR-0.

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

Я вообще считаю, что такое однозначное соответствие класс — файл это источник проблем для PHP. Помимо сложностей с функциями появляются дополнительные затраты на создание маленьких классов. Грамотное ООП зачастую порождает большое количество небольших классов, что хорошо с точки зрения поддержки, повторного использования кода и тестирования. Но в PHP для каждого классика надо заводить свой файл. И это по-настоящему бесит. Меня совершенно не смутит десяток коротких классов, живущих в одном файле. Но создавать десять файлов для каждого из них мне реально кажется неэффективным (и осложняющим поддержку).

Заодно упомяну еще один “общепринятый” подход, который мне не нравится, это избыточные блоки комментариев. В львиной доле случаев phpdoc ставит сам капитан Очевидность, код от них распухает в два-три раза. Ничего не имею против доблоков там, где они в самом деле необходимы, но в большинстве случаев (по крайней мере в хорошем с архитектурной точки зрения коде) название метода и параметров говорят сами за себя.

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

Такие вот мысли. Может я вообще все не так понимаю?
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 60

    +2
    Так… куда все-таки складывать хэлперы?
      +10
      Ну хелперы я лично складываю в классы с определенным названием, допустим StringUtils, Utils, ArrayUtils или что-то в этом роде. Мне лично гораздо легче обратиться в начале к классу, который по сути дела отвечает за определенную тематику, нежели искать эту чертову функцию черт знает где и с каким именем ещё не известно.

      Плюс аутолоадинг происходит для классов с определенным именем, а функции нужно как-то объявлять в отдельном файле и инклюдить (можно все объявить до/после класса, но это как минимум странно)
        +3
        В том же Laravel файл с хелперами (коих во фреймворке предостаточно) лежит в src/Illuminate/Support/helpers.php и подключается через composer.json

            "autoload": {
                "classmap": [
                    "src/Illuminate/Queue/IlluminateQueueClosure.php"
                ],
                "files": [
                    "src/Illuminate/Support/helpers.php"
                ],
                "psr-0": {
                    "Illuminate": "src/"
                }
            },
        

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

        Вообще то, чаще всего phodoc используют для автоматической генерации документации и автокомплита в ide.

        Классы записывают в отдельные файлы из-за автозагрузчика. Ведь в пыхе нам не надо явно импортировать модули. И искать нужные классы для работы с ними так легче.

        Как по мне, статья ниочем и покащывает только личное мнение автора, без каких либо притензий на объективность.
          +2
          Я раньше держал папку Helpers, где были хелперы сгрупированные по схожим функциям. В каждом хелпере был просто набор функций. Сейчас похожее решение, только использую Yii и хелперы реализованы в виде статических методов.
            0
            И да начнётся холивар.
            Про огромные PHPDoc'и согласен — во многом это дело капитанское, но: работая в хорошей IDE'шке, избежать таких тегов как
            @param @return @description @warning @warning @since и @author никак не получится. Это всё же увеличивает скорость нахождения и определения назначения метода, работая при это даже в другом файле. И как бы код не разрастался, приходиться смиряться, ибо это упрощает читаемость.
              +2
              Аналогично, на всех проектах активно используем PHPDocs просто потому, что в связке с PHPStorm они позволяют заметно упростить работу, особенно мелочи, вроде @property className[] $propertyName и подобные. Кроме того, все такие комментарии можно автоматически свернуть, если они мешают восприятию кода.
                +1
                Да и в том-же PhpStorm можно сразу (по Ctrl+Q, если не менять) открыть отдельное окно с документацией по классу/методу и пр., которая генерируется на основе PHPDoc, что частенько бывает полезно.
              +6
              Зачем ЭТО на Хабре?
                –3
                Поясните, пожалуйста.
                  +5
                  Не могу точно утверждать за автора коммента. Но думаю, он хотел сказать, что на хабре не место капитанской статье в которой поверхностно пробегается по нескольким проблемам (проблемам ли?) и не приводится ни одного пути их решения.
                    +1
                    Так-то я совершенно не против ваших доводов и автора статьи не поддерживаю. Просто, на мой взгляд, комментарий к статье на Хабре должен быть чем-то большим, чем просто негативный высер, достойный группы из ВК ИМХО.
                      +1
                      Тут я с вами полностью согласен.

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

                      Хотя в последнее время появляется куча бессмысленных комментов со «смишными картинками» и прочим хламом.
                    –1
                    Обычно такие посты пишут для своих блогов, это не уровень Хабра
                      +5
                      А «Три способа решения проблемы «Fatal error: Allowed memory size of XXX bytes exhausted»» это уже уровень хабра?)
                      Да ещё и с вашими же комментариями

                      Не все настолько опытные и продвинутые в программировании, поэтому этот пост (первый на Хабре) скорее для новичков, нежели для проженых товарищей!

                      Засрать может любой, а вот принять ;) Странные Хабра-люди.

                      Если человеку интересен этот пост — значит он об этом не знал. Буду рад, если это пригодится кому-то.
                        –1
                        Во-первых, это было 4 года назад, во-вторых, я после этого поста ничего не пишу на Хабре, только в свой личный блог.
                          +1
                          Хабр — уже не торт!
                          И я без сарказма говорю. Раньше были серьезные, глубокие технические статьи. Прошлым летом обратил внимание, что как то читать нечего. Одни новости. К примеру сейчас с завидной регулярностью стали появляться заметки (статьями это не назовешь) «вышла такая-то игра». Вы раньше часто встречали такое?

                          Как мне кажется хабр из сборника технических статей понемногу превращается в сборник новостей. Нельзя сказать, что это плохо. Просто меняется направление в угоду аудитории. Но я захожу сюда за интересными статьями. А их все меньше и меня это расстраивает.
                            0
                            Я согласен, что хороших статей меньше и меньше, а «стартаперы из Лаоса поставили Андроид на пылесос» всё больше и больше.

                            Но я и не говорю, что это хорошая статья и она нужна. Просто это странно было читать от человека, у которого в постах висит точно такая же.
                              0
                              Ну, на мой взгляд, серьезные и глубокие статьи всё же появляются, но да, количество материала рекламного характера несколько зашкаливает.
                      –1
                      Мне фасады в laravel нравятся :)
                        0
                        Вообще, я бы не занимался переводом статей данного автора. Может я чего не знаю, но мне кажется, что уровень его компетентности оставляет желать лучшего. Я не говорю, что он — дурак. Но полезного мало.
                        Ни разу не видел статьи от него, с хорошим и обоснованным анализом, подкрепленную хоть какими-нибудь авторитетными источниками. Все какие то пустые рассуждеения ниочем. Эта статья — яркий пример большинства его контента.
                        Лучше что-нибудь полезное переведите.
                          +5
                          Например, вот его: www.phpinternalsbook.com/
                            +1
                            А можно примеры не компетентности Никиты (Nikic)?

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

                            Статью нужно обдумать, пока что не согласен с предложенным использованием функций. Но Nikic попросту воду лить не будет.
                              +1
                              Никита Попов внес вклад в той или иной мере (от продвижения дискуссии до непосредственно внедрения функционала) в подавляющее большинство нововведений в PHP начиная с 5.4. В вопросах внутренностей PHP вряд-ли найдется человек более компетентный чем он, который активно пишет статьи.

                              Можно поинтересоваться вашими достижениями, компетентностью в вопросе?
                              +3
                              Небольшое уточнение: в java один public класс на один файл. Вдобавок к нему в этом же файле может быть сколько угодно классов с пакетной видимостью.
                                +1
                                … а так же любое количество вложенных public static классов, инстансы которых можно создавать без каких-либо проблем ;)
                                +2
                                Кто вам, собственно, мешает написать свой автолоад, который умеет подгружать «модули»?
                                  +1
                                  Ну для этих целей вполне себе подойдет Composer, как уже писали выше, с его разделом «files».
                                    +1
                                    Это будет по сути замена инклуду со всеми вытекающими, прежде всего с загрузкой всех функций, а не только необходимых, при каждом обращении.
                                  +6
                                  Автозагрузка для функций есть в PHP 5.6, но де-факто функции в php для пользовательского кода особо и не нужны. Их заменили классы-хелперы со статичными методами, они выполняют роль модулей, которые хранят набор «функций». И это намного удобнее и более организованно, а не попытка угнаться за модным ООП, тут его собственно и нет.
                                    +1
                                    Зачем городить классы со статик-методами, если есть неймспейсы?
                                      +3
                                      А причём тут неймспейсы?
                                        –4
                                        А зачем ради набора статик-функций городить класс?
                                          0
                                          Вы не ответили на мой вопрос — причём здесь неймспейсы. Расскажите, как Вы используете неймспейсы для создания хелперов, вместо классов, а я Вам в ответ расскажу о том, зачем городить классы.
                                            0
                                            Изначально речь шла о том, что мол пихать функции в классы статик-методами «удобнее и более организованно»
                                            Так вот для «более организованно» есть неймспейсы.
                                            Теперь рассказывайте
                                              +1
                                              Классы-хелперы можно тоже запихнуть в неймспейс, вот только подгружаться они будут автоматически, без лишней возни с composer/include/require и т.д.
                                                0
                                                Не будут. Возня всё равно нужна, чтобы настроить автозагрузку классов, особенно если схема расположения не тривиальная.
                                                  0
                                                  Я решал эту проблему с возней через class scanner, он сам автоматически искал классы в classpath директориях, которые ему задаешь. Получалось очень юзабельно, но конечно оверхед, хоть и небольшой, но есть. Не задумываешься о том, по какому принципу нужно подключать какую-то левую библиотеку.
                                      0
                                      Это забивание гвоздей микроскопом, причём при наличии молотка. Классы-хелперы часто вырождаются либо в классы одного метода, либо в классы с кучей никак не связанных методов, поскольку в подавляющем большинстве случаев хелперы являются чистыми функциями, никак не связанными с другими.

                                      А для организации кода предназначены как раз нэймспэйсы. Единственное, что в PHP нет нормального механизма автозагрузки функций.
                                        0
                                        Автозагрузка для функций есть в PHP 5.6

                                        Где об этом почитать? Нашёл только об импорте функций и констант из нэймспэйсов, но, как я понял, пути зарузки предлагаются традиционные — require до вызова.
                                          –1
                                          Ну да, все традиционно через require в автозагрузчике. Однако проблема в том, что 1 функцию или в константу в каждом отдельном файле держать не будешь и поэтому нужно как-то ухищряться.

                                          Вот ссылка: wiki.php.net/rfc/use_function
                                            +1
                                            Это про импорт из нэймспэйса, а я про автозагрузку. И можно держать в отдельном файле (не вижу проблем), и можно настраивать мэппинг нескольких функций на один файл (хотя это вариант не очень, имхо), но автозагрузки функций нет как сущности.
                                              0
                                              А теперь я понял, т.е. нет специального механизма типа spl_autoload_register отдельно для функций. Да это странновато конечно =(
                                          0
                                          Насколько я помню предложение о введении автозагрузки функций было отвлонено за отсутствием адекватного решения.
                                          +8
                                          Зачем Вы перевели статью за 10 августа 2012 года?
                                            +1
                                            Не любовь использования функций в PHP связана с проблемой автозагрузки.
                                            Нельзя просто так взять и загрузить функцию. А держать один файл на все функции хелперы — путь к хаосу.

                                            В Python же модули решают все проблемы. Однако, из-за этого разработчики часто увлекаются функциями, поэтому очень тяжело изучать всю эту динамику статически.
                                              0
                                                +1
                                                Разработчик не боится функций, он их презирает.
                                                  0
                                                  На мой взгляд, описанные проблемы надуманы.
                                                  Комментариями код не испортишь. Они без проблем игнорируются при беглом осмотре за счет подсветки синтаксиса в редакторе кода. О причине предпочтения классов функциям сказано выше.

                                                  Вот о чем, на мой взгляд следовало бы сказать — это повальное увлечение PHP-сообщества ассоциативными массивами. В 9-ти случаях из 10-ти использование массивов не уместно.
                                                  Причем люди пришедшие в php из других ООП языков, таких как C++ или Java, обычно замечают эту странность.
                                                  Поясню. Например, в C++ тоже есть ассоциативные контейнеры, такие как std::map и std::unordered_map. Но никому не придет в голову использовать эти контейнеры там, где уместно было бы определить тип данных. Т.е. создать класс.
                                                  Эта тенденция является продолжением стиля программирования порождаемого отсутствием статической типизации. Но в данном конкретном случае средства языка явно позволяют писать более аккуратный и читаемый код. Поскольку классы в php все же определят пользовательский тип данных, но программисты все равно продолжают использовать ассоциативные массивы вместо классов.
                                                  Кстати, нет никаких причин не помещать мелкие классы порождаемые от одного и того же интерфейса в один файл.
                                                    0
                                                    Вы имеете в виду, что пишут
                                                    $user = [
                                                        'name'=>'userName',
                                                        'password'=>'userPassword',
                                                    ];
                                                    

                                                    вместо
                                                    class User
                                                    {
                                                        public $login;
                                                        public $password;
                                                    }
                                                    $user = new User();
                                                    $user->name = 'userName';
                                                    $user->password = 'userPassword';
                                                    

                                                    Я правильно понял?
                                                      +2
                                                      Примерно так. На первый взгляд кажется, что кода стало больше, но на самом деле (если не вырывать кусок кода из контекста), читаемость от такого подхода улучшится.
                                                      А еще лучше было бы вообще не использовать публичные поля, а использовать гетеры и сетеры.
                                                        0
                                                        Еще преимущество второго способа — то, что при передаче такого объекта в фукнцию, чтожно явно указать, что нужен именно он, а не какой то абстракный массив.
                                                        function someFnct(User $user) { ... } лучше для поинмания, чем function someFnct(array $user) { ... }
                                                          0
                                                          Так это у людей проходит, когда они нормальные книги начинают читать, а не уроки Васи Пупкина «Как создать сайт за 2 недели».
                                                        0
                                                        Во многих случаях это обусловлено как раз нежеланием создавать отдельный файл для «одноразового» Data Transfer Object. Причём не столько нежелание создавать его физически, сколько думать об его именовании, положении в архитектурной иерархии, загрузки перед использованием. Плюс удобные методы доступа к элементам в стандартной библиотеки (без них можно было бы обойтись вместо $data = []; $data['value'] = 'value'; чем-то вроде $data = new StdClass; $data->value = 'value';).

                                                        Что до мелких классов в одном файле с интерфейсом, то как вы представляете реализацию их автозагрузки? Для каждого класса указывать файл для загрузки? Или дополнять имя класса суффиксом типа ImplemetsSomeInterface, чтобы загрузчик знал что загружать его надо из SomeInterface.php? Насколько гибко и универсально такое решение получится?
                                                          0
                                                          На самом деле ваш комментарий не опровергает мой, а подтверждает: увлечение ассоциативными массивами является проблемой, но php-сообщество ее не замечает.
                                                          Да, писать код с ассоциативными массивами вместо классов, вероятно, быстрее. И в этом даже нет ничего плохого, если вы пишите короткий скрипт или код предназначенный только для вас. Но если ваш код придется кому то читать (а код все же чаще читают чем пишут), то вы просто перекладываете проблемы на чужие плечи.
                                                          Смысл очень простой — ассоциативные контейнеры предназначены не для замены ООП, а для совсем других целей. Кроме того, использование ассоциативных массивов вместо классов лишает ваш код тех и без того не больших возможностей статической типизации, которые дает php.
                                                            0
                                                            Ну, в PHP ассоциативные массивы появились раньше чем ООП и потому о «замене» говорить не приходится в принципе.

                                                            Есть задача передачи набора именованных значений и в современном PHP её можно решить минимум тремя очевидными способами: ассоциативный массив, «статическое» описание класса (по сути структуры данных, аналогичной struct в Си или record в Pascal) и динамическим созданием свойств у произвольного объекта (например StdClass). Все они имеют свои плюсы и минусы и выбирать нужно согласно текущим приоритетам.
                                                        0
                                                        Но в PHP для каждого классика надо заводить свой файл.

                                                        Не хотите — не заводите.

                                                        Меня совершенно не смутит десяток коротких классов, живущих в одном файле.

                                                        У всех есть свои предпочтения. Кому-то проще ориентироваться по файлам, кто-то привык использовать «Jump to definition».
                                                        Без последней опции (а она, собственно, даже учитывая плагины, не в каждом редакторе найдется) мне лично будет некомфортно в файле из винегрета классов и функций.
                                                          +1
                                                          Что-то никто не написал, что в PHP 5.6 ввели импорт функции из нэймспэйса:

                                                          www.php.net/manual/en/language.namespaces.importing.php
                                                          PHP 5.6+ also allows aliasing or importing function and constant names.

                                                          www.php.net/manual/en/migration56.new-features.php#migration56.new-features.use
                                                          The use operator has been extended to support importing functions and constants in addition to classes. This is achieved via the use function and use const constructs, respectively.

                                                          Так что статья уже не актуальна.
                                                            0
                                                            да потому что статья за 2012 год. Кстати, автор функционала и есть автор статьи (не перевода).
                                                              0
                                                              Импорт функций без автозагрузки особо положения дел не имеет.

                                                            Only users with full accounts can post comments. Log in, please.