Так получилось, что в последние несколько лет я сшиваю Франкенштейнов, а не ваяю милые фарфоровые статуэтки пастушек и трубочистов. Я создаю решения на базе Magento 2. Это значит, что исходный материал у меня — мечта любого археолога. Культурный слой со следами различных "эпох" и "цивилизаций". По нему можно изучать развитие программистской мысли в PHP/JS сообществах в течение последнего десятилетия.


И это только базис, а надстройка — сторонние модули, которые нужно интегрировать вовнутрь. Вот тут уже можно столкнуться с проявлениями внеземного разума. Некоторые модули созданы развитыми существами, очень похожими по мышлению на создателей базиса, но попадаются такие, что хочется приобнять автора за плечо, проникновенно заглянуть ему в глаза и по-дружески так спросить: "Ты с какой планеты, родной?"


image


Сшить Франкенштейна из такого материала помогает отладчик (debugger). Ниже идёт мой персональный топ приёмов кодирования, которые способны усложнить жизнь любому, кто, как и я, ежедневно использует отладчик в своей жизни. Он небольшой, на четыре позиции, но каждый раз, когда я сталкиваюсь с подобным при отладке — я печалюсь. Может быть мой пост уменьшит количество скорбей в мире, а может и нет. Я, по крайней мере, попытаюсь.


Обфускация и шифрование кода


Это вне конкурса. Я пару раз сталкивался с модулями, для работы которых нужен ionCube, и могу сказать, что последнее, что я сделаю — поставлю в свой проект подобный модуль. Я полностью поддерживаю минификацию JS-кода, особенно, когда рядом идёт обычный исходник, но обфускация и шифрование — дистиллированное, концентрированное зло. Это я вам как Интегратор говорю.


IV. Однострочный код


Экономия на строчках кода — самое безобидное в моем списке:


if ($cond) aaa(); else bbb();

Зависание на два шага на этой строке при пошаговом выполнении программы (вычисление условия, выполнение ветки true или false). Ничего страшного, нужно просто держать в голове, сколько раз ты выполнил step-over на этой строке, и отслеживать значение $cond в списке переменных. Со временем нарабатывается автоматизм.


Чуть хуже то, что ты не можешь поставить безусловную точку останова (breakpoint) на ветке true или false. Придётся вместо одного клика в IDE поработать мышкой/клавиатурой чуть подольше, добавляя условную точку останова.


Идеальный вариант, когда каждый исполняемый шаг (условие, true-ветка, false-ветка) находится на своей строке:


if ($cond)
    aaa();
else
    bbb();

III. Результат-выражение


Использование выражений в условиях:


if (exp()) ...

циклах:


foreach (exp() as $item) ...

в качестве параметров:


foo(exp(), ...)

и возвращаемого результата:


return exp();

не только делает код "плотнее", облегчая его понимание, но и затрудняет отладку — ты просто не видишь значений выполнения выражений в списке переменных отладчика. Приходится добавлять watches (интересный вопрос, а если мониторить генераторы через watches, повлияет ли это на выполнение программы?).


Идеальный вариант — временная переменная:


$exp = exp();
if ($exp) ...

II. Множество точек выхода


Я множество раз сталкивался с рекомендацией иметь только одну точку выхода из функции и множество раз сталкивался с нарушением этой рекомендации (пример выдуманный, но типовой):


public function onEvent($event) {
    if($event == 'entrance') {
         return 'entranceRoute';
    } else if($event == 'exit') {
         return 'exitRoute';
    }
     return 'defaultRoute';
}

Вот более правильный вариант:


public function onEvent($event) {
    $result = 'defaultRoute';
    if($event == 'entrance') {
        $result = 'entranceRoute';
    } else if($event == 'exit') {
        $result = 'exitRoute';
    }
    return $result;
}

Всё, мне не нужно разбрасывать точки останова на каждом return или делать step-out с первой строки (если вызывающий код даёт мне возможность посмотреть результат в отдельной переменной), чтобы понять, чем закончилось выполнение. Представляете себе функцию на 120 строк и 22 return'а внутри? А я такую собственноручно дебажил и подозреваю, что это не предел.


I. Каскадный вызов методов


Мой фаворит — method cascading:


$collection
    ->addFilterByProduct(...)
    ->addShowInStoresFilter(...)
    ->addPublicFilter(...)
    ->addApprovedStatusFilter(...)
    ->addCreatedAtLessThanNowFilter(...);

Если мне нужно попасть внутрь метода addApprovedStatusFilter(), который является интерфейсным и имплементирован в нескольких различных классах (конкретный класс определяется во время выполнения), то самое простое — поставить точку останова на $collection и по-очереди проходить всё (addFilterByProduct, addShowInStoresFilter, addPublicFilter) вплоть до нужного места. Если соединить это с использованием выражений в параметрах и возвращаемых результатах, то путь становится совсем не близкий. В оригинале данный код выглядит так:


$collection
    ->addFilterByProduct($this->getProduct())
    ->addShowInStoresFilter($this->_storeManager->getStore()->getId())
    ->addPublicFilter()
    ->addApprovedStatusFilter()
    ->addCreatedAtLessThanNowFilter();

Да, с каскадированием методов код становится более читаемым, но дебажить его становится сложнее. Ничего не имею против каскада set'еров (я, как правило, не дебажу сеттеры):


$answerModel
    ->setAuthorName(...)
    ->setStatus(...)
    ->setCreatedAt(...)
    ->setAuthorEmail(...)
    ->setCustomerId(...)
    ->setHelpfulness(...)

Но код, который содержит логику, который может быть придётся дебажить, а может быть даже самому, лучше писать "олдскульно" (я сам так и делаю):


$collection->addFilterByProduct(...);
$collection->addShowInStoresFilter(...);
$collection->addPublicFilter(...);
$collection->addApprovedStatusFilter(...);
$collection->addCreatedAtLessThanNowFilter(...);

Намного сложнее для восприятия стало?


Или вот рекомендации хорошего стиля программирования:


ladder.up().up().down().up().down().showStep();

Посмотрите на это с точки зрения интегратора, который должен залезть внутрь второго down'а.


Резюме


Я не имею в виду, что данные приёмы используют в своём коде "инопланетяне". Вовсе нет, в большинстве своём они, наоборот, направлены на сокращение количества кода. Но данные приёмы осложняют отладку. Человек не обладает скоростью компьютера и, чтобы выйти на проблемный код, интегратор должен сначала просканировать (не понять, а именно просканировать) ход выполнения программы в ключевых точках. Всё вышеперечисленное, имея с одной стороны задачу улучшить понимание кода, по факту, во время отладки, этому самому пониманию мешают. Мешают не в месте расположения кода, а мешают добраться до проблемного участка, который, в общем, и нуждается в настоящем понимании.


Disclaimer


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

Комментарии (43)


  1. VolCh
    13.03.2019 12:06

    Должно быть


    $tmp = $collection->addFilterByProduct(...);
    $tmp = $tmp->addShowInStoresFilter(...);
    $tmp = $tmp->addPublicFilter(...);
    $tmp = $tmp->addApprovedStatusFilter(...);
    $tmp->addCreatedAtLessThanNowFilter(...);
    unset($tmp)

    для полной (почти) эквивалентности :)


    А в целом, "более правильный" нужно заменить "более удобный для отладки отладчиком". :)


    P.S. Я сторонник раннего выхода из функции/метода при валидации параметров и других подобных проверок. А для ситуаций типа onEvent() — вообще массиві или другие map структуры предпочитаю использовать типа $result = self::eventResultMap[$map] ?? 'defaultResult'


    P.P.S. А тернарки как, не доставляют?


    1. flancer Автор
      13.03.2019 14:11

      "более удобный для отладки отладчиком"

      Согласен.


      А тернарки как, не доставляют?

      Доставляет всё, где нужно хоть чуть-чуть думать своим мозгом при трассировке. Я предпочитаю видеть результат выполнения программы глазами, а не мозгом :)


  1. vlreshet
    13.03.2019 12:09

    Идеальный вариант, когда каждый исполняемый шаг (условие, true-ветка, false-ветка) находится на своей строке:
    if ($cond)
        aaa();
    else
        bbb();

    По-моему, идеальный вариант это когда каждая ветка условия имеет свой логический блок
    
    if ($cond) {
        aaa();
    } else {
        bbb();
    }
    


  1. transcengopher
    13.03.2019 14:15

    Интеграторы против Builder Pattern?


    1. flancer Автор
      13.03.2019 14:37

      Нет, вам показалось.


    1. Danil1404
      13.03.2019 15:14

      Это же не builder, а method chain.


      1. transcengopher
        13.03.2019 15:21

        Согласен. Сработало искажение на базе моего опыта — я не припомню Builder'ов, которые не позволяли бы вызывать методы в цепочке, особенно те, что должны быть «одноразовые», потому я почти перестал различать эти два паттерна.


  1. bm13kk
    13.03.2019 20:48

    Я понимаю боль от дебага функций раннего выхода.
    Но
    1) метод на 120 строк — по определению боль
    2) читаемость кода важнее удобности дебага. Единый ретурн ухудщает читаемость.
    У тебя есть метод. в определенный моент он ветвится. Психологически легче быстро отработать короткий случай и забыть о нем.
    3) как советовали выше — используйте другой дебаггер. Посмотрите прогресс ИДЕИ за последний год — там много фич как раз для дебага.
    Да, у вас не получится однокликовое решение. Но работая с маджентой — вы себе сами предопределили условия
    4) возможно наибыстрейшее решение — это банальное редактирование кода. Одна комбинация клавиш и код теперь дебажится. А потом все откатить через CVS.


    1. flancer Автор
      13.03.2019 21:27

      Согласен со всем, кроме:


      2) читаемость кода важнее удобности дебага. Единый ретурн ухудщает читаемость.
      У тебя есть метод. в определенный моент он ветвится. Психологически легче быстро отработать короткий случай и забыть о нем.

      При чтении кода возврат в третей-пятой строке кода не отменяет необходимости читать метод до конца, чтобы его понять. Это при отладке можно выскочить на третей-пятой строке по return'у и не парится, что там далее (если условия так сработали), а для понимания всего метода нужно читать метод целиком. В таком случае


      ....
      $result = 1234;
      ...
      return $result;

      ненамного хуже для читабельности, чем


      ...
      return 1234;
      ...

      если, разумеется, использовать для возврата переменную $result по-умолчанию. К тому же единая точка возврата дисциплинирует, заставляя надёжнее изолировать различные результаты через if-else, что весьма пригождается при рефакторинге. Но это мне мой опыт так говорит, у вас он может быть другим.


      1. bm13kk
        14.03.2019 00:01

        Мой опыт другой.
        Смотрите. Если использовать переменную резалт — то возможно ее кто-то где-то как-то изменит. Если и правда изменит — значит это уже другой метод.

        if ... $res = 'value';
        ....
        $res += '-postfix';
        ....
        return $res;
        

        должно стать
        private function calculate() {
            if ... return 'value';
             ...
             return ...;
        }
        public function get() {
             return $this->calculate() + '-postfix';
        }
        


        Делая ранний ретурн — ты гарантируешь что логика метода только про это. Что артефектов с результатом потом не будет.

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

        Единственный случай, когда единая точка выхода хороша — настоящая функциональщина. Но сюда не относится ни пхп ни маджента. Более того, функциональщина в корне меняет стиль написания. И почти всегда, иф — это будет отдельный метод или последний операнд.

        [любоя практика] дисциплинирует

        Ужастный подход к программированию и процессам. Дисчиплинировать должны анализаторы, компиляторы и прочие гейты в СИ. Такой подход, это как сказать «я не буду писать хорошо, чтобы читатель не расслаблялся и был готов самостоятельно найти ошибки».


        1. flancer Автор
          14.03.2019 07:38

          Делая ранний ретурн — ты гарантируешь что логика метода только про это.

          Делать единственный возврат — вот это гарантия, что логика метода только про это. Ваш пример, переписанный для одной точки возврата:


          if ($cond){
              //...
              $res = 'value';
          } else {
              //...
          }
          return $res;

          Такой подход заставляет читать код метода до конца, а не до первого return'а, и понимать его во всей полноте, а не только "основную логику" (или "наиболее часто встречающуюся"? или "самую тривиальную"? — что там обычно первым возвратом ставят?).


          я не буду писать хорошо

          Что такое хорошо и что такое плохо — зависит от контекста. Никто не может написать код, который хорош во всех контекстах. Лично я пишу не для того, чтобы мой код легко читался, а для того, чтобы он проще модифицировался.


          1. VolCh
            14.03.2019 11:56

            Делать единственный возврат — вот это гарантия, что логика метода только про это.

            Как раз не гарантия. Хорошо если визуально сразу видно две ветки, а что если их 10 и они не выделены как полноценные альтернативы? Типа


            (if ($cond){
                $res = 'value';
            }
            
            if (!$cond) {
                // a lot of code
                $res='anotherValue';
            }
            return $res;


            1. flancer Автор
              14.03.2019 12:18

              В таком случае ставьте return'ы — я буду дебажить с первой строки метода. Со временем ваш код может превратиться в подобие этого:


              сэволюционировавший код
              public function isApplicable()
              {
                  $p = $this->getProduct();
                  if (!$p) {
                      return false;
                  }
                  // has image for the current mode
                  if (!$this->getValue('img'))
                      return false;
                  $now = Mage::getModel('core/date')->date();
                  if ($this->getDateRangeEnabled() && ($now < $this->getFromDate() || $now > $this->getToDate())) {
                      return false;
                  }
                  // individula products logic
                  $inArray = in_array($p->getSku(), explode(',', $this->getIncludeSku()));
                  if (!$p->getSku())
                      $inArray = false;
                  // include skus
                  if (0 == $this->getIncludeType() && $inArray)
                      return true;
                  // exclude skus
                  if (1 == $this->getIncludeType() && $inArray)
                      return false;
                  // use for skus only
                  if (2 == $this->getIncludeType())
                      return $inArray;
              
                  if ($this->getPriceRangeEnabled()) {
                      switch ($this->getByPrice()) {
                          case '0': // Base Price
                              $price = $p->getPrice();
                              break;
                          case '1': // Special Price
                              $price = $p->getSpecialPrice(); // or $this->_info['special_price']
                              break;
                          case '2': // Final Price
                              $price = Mage::helper('tax')->getPrice($p, $p->getFinalPrice());
                              break;
                          case '3': // Final Price Incl Tax
                              $price = Mage::helper('tax')->getPrice($p, $p->getFinalPrice(), true);
                              break;
                          case '4': // Starting from Price
                              $price = $this->_getMinimalPrice($p);
                              break;
                          case '5': // Starting to Price
                              $price = $this->_getMaximalPrice($p);
                              break;
                      }
                      if ($p->getTypeId() == 'bundle'){
                          $priceMin = $p->getMinPrice();
                          $priceMax = $p->getMaxPrice();
                          if ($priceMin < $this->getFromPrice() && $priceMax > $this->getToPrice()) {
                              return false;
                          }
                      }
                      else if ($price < $this->getFromPrice() || $price > $this->getToPrice()) {
                          return false;
                      }
                  }
                  $attrCode = $this->getAttrCode();
                  if ($attrCode) {
                      if (!array_key_exists($attrCode, $p->getData())) { // has attribute condition
                          return false;
                      }
                      // compatibility with the `Amasty: Custom Stock Status` extension
                      // if the `Use Quantity Ranges Based Stock Status` property setted to `Yes`
                      // so the value of the `Custom Stock Status` is dynamic
                      // and setted to the product value is not used
                      if (('custom_stock_status' === $attrCode) && (Mage::helper('core')->isModuleEnabled('Amasty_Stockstatus'))) {
                          if ($this->getAttrValue() != Mage::helper('amstockstatus')->getCustomStockStatusId($p)) {
                              return false;
                          }
                      } elseif ($this->getAttrValue()) {
                          $v = $p->getData($attrCode);
                          if (preg_match('/^[0-9,]+$/', $v)){
                              if (!in_array($this->getAttrValue(), explode(',', $v))){
                                  return false;
                              }
                          }
                          elseif ($v != $this->getAttrValue()){
                              return false;
                          }
                      } elseif (!$p->getData($attrCode)) { // sometimes needed for has attribute condition too
                          return false;
                      }
                  }
                  $catIds = $this->getCategory();
                  if ($catIds) {
                      $ids = $p->getCategoryIds();
                      if (!is_array($ids))
                          return false;
                      $found = false;
                      foreach (explode(',', $catIds) as $catId) {
                          if (in_array($catId, $ids))
                              $found = true;
                      }
                      if (!$found)
                          return false;
                  }
                  $stockStatus = $this->getStockStatus();
                  if ($stockStatus){
                      $inStock = $p->getStockItem()->getIsInStock() ? 2 : 1;
                      if ($inStock != $stockStatus)
                          return false;
                  }
                  if ($this->getIsNew()){
                      $isNew = $this->_isNew($p) ? 2 : 1;
                      if ($this->getIsNew() != $isNew)
                          return false;
                  }
                  if ($this->getIsSale()){
                      $isSale = $this->_isSale() ? 2 : 1;
                      if ($this->getIsSale() != $isSale)
                          return false;
                  }
                  if ($this->getCustomerGroupEnabled() && ('' === $this->getCustomerGroups() || // need this condition, because in_array returns true for NOT LOGGED IN customers
                      (!in_array(Mage::getSingleton('customer/session')->getCustomerGroupId(), explode(',', $this->getCustomerGroups())))))
                      return false;
                  // finally ...
                  return true;
              }


              1. VolCh
                14.03.2019 12:45

                Любой код может превратиться в такой. Сделаете c одной точкой возврата, а вам добавят промежуточных. Причём он станет ещё сложнее для понимания и изменения, чем изначально с ранними возвратами, потому что два подхода будет смешано.


                1. flancer Автор
                  14.03.2019 12:51

                  Правильно, потому что чья-то "религия" позволяет ставить return'ы где ни попадя, а чья-то нет. В результате имеем микс. С одной точкой возврата получится такая вложенность по if'ам, что придётся разбивать проверку на связные части. Придётся делать код, более устойчивый к модификациям, т.к. придётся глубже нырять в предметную область и лучше понимать её. Поставить ещё один return гораздо проще.


                  1. VolCh
                    14.03.2019 14:01

                    А у кого-то есть компромиссы типа ранние возвраты только до основной логики. И никаких «придётся» нет, если никто или ничто не следит за вложенностью и т. п.


  1. RSM
    14.03.2019 01:48

    Не используй код код который недостаточно документирован или который ты не можешь понять прочитав (ну или на крайний случай натравив автоформаттер и прочитать). Тут два два варианта: или код очень плох или ты слишком туп что бы понять его. Оба варианта не годятся — или ты добавишь ложку говна в бочку меда или наоборот — ни первое, ни второе не принесут пользы. Дебаггер исправить ситуацию не поможет :)


    1. flancer Автор
      14.03.2019 07:07

      Не используй код код который...

      Отличный совет! Жаль, что интеграторы не могут им воспользоваться.


  1. Bluz
    14.03.2019 09:56
    +1

    Ой, про единую точку возврата уже накидали, но хочется ещё подискутировать не эту тему.

    Читать вот это

    function foo(baz) {
        if (baz < 0) {
            return 0;
        }
    
        //  ...
        //  do any for result
        return result;
    }


    Много проще чем это,
    function foo(baz) {
        if (baz < 0) {
            result = 0;
        } else {
            //  ...
            //  do any for result
        }
    
        
        return result;
    }


    особенно если проверок может быть много
    function foo(baz) {
        if (baz < 0) {
            result = 0;
            
        } else if(baz > 100 && baz <= 1000) {
            result = do_f1();
            
        } else if (baz > 1000 && baz <= 10000) {
            result = do_f2();
        
        } else if (baz > 10000 && baz <= 100000) {
            result = do_f3();
        
        } else{
            result = do_f4();
        }
        
        return result;
    }


    Эта гирлянда вообще вызывает ассоциации с ктулху.

    А уж если как-то так получается, что иерархия if/else'ов уходит вглубь на пару уровней, то совсем тушите свет, потому что третьему уровню контекст теряется: кто и зачем туда попал?


    1. flancer Автор
      14.03.2019 10:08
      -1

      Если вы пишите код для того, чтобы его читать, то да — так удобнее. Особенно, если для вас замена $result на return превращает ктулху-гирлянду в true-code-гирлянду:


      function foo($baz)
      {
          if ($baz < 0) {
              return 0;
      
          } else if ($baz > 100 && $baz <= 1000) {
              return do_f1();
      
          } else if ($baz > 1000 && $baz <= 10000) {
              return  do_f2();
      
          } else if ($baz > 10000 && $baz <= 100000) {
              return  do_f3();
      
          } else {
              return  do_f4();
      
          }
      }

      Лично у меня return из начала-середины метода вызывает ассоциации только с goto.


      1. vlreshet
        14.03.2019 10:46
        +1

        Если вы пишите код для того, чтобы его читать
        Именно!!! Именно для этого я пишу свой код! Машине пофиг какой код исполнять, а мой код буду читать я, и мои коллеги. Читаемость кода — наше всё!


        1. flancer Автор
          14.03.2019 10:53
          -1

          А я пишу код для того, чтобы его изменять. Изменять код буду я сам и мои коллеги. Изменяемость написанного кода — вот это "наше всё!" Я читал в детстве некоторые образчики perl-кода — ничего не понятно, но красиво-о-о. Занимательное чтиво.


          1. VolCh
            14.03.2019 12:26

            Думаю, всё-таки, перед изменениями вы код читаете :) И, вполне вероятно, читаете чаще чем изменяете.


            1. flancer Автор
              14.03.2019 12:46

              Вы правы, читаю я чаще, чем изменяю. Но читаю я именно для того, чтобы понять, где и что мне нужно изменить (или что здесь ничего менять не нужно). Код разбивается на классы/процедуры/функции не потому, что человек не может прочесть 2К строк подряд ("Войну и Мир" многие осилили). А для того, чтобы ограничить влияние изменений, особенно неправильных. Так что говорить, что мы пишем код для того, чтобы его читать — в корне не верно. Мы читаем, чтобы понимать. А понимаем, чтобы изменять. В противном случае было бы достаточно один раз написать код и один раз тесты к нему. Поэтому код должен быть не легко читаем, а легко изменяем. Кстати, удобнее читать код, который написан в одном файле, чем скакать от файла к файлу и обратно.


              1. VolCh
                14.03.2019 14:14

                Под чтением я, естественно, имею в виду чтение с пониманием, а не то как я латынь читаю :)

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


                1. flancer Автор
                  14.03.2019 14:18

                  А для чего вам понимание кода?


                  1. VolCh
                    14.03.2019 14:29

                    Самое частое, наверное — найти правильное место для изменений. Трудоемкость самих изменений — дело десятое.


                    1. flancer Автор
                      14.03.2019 16:03

                      Кстати, в тему свежая публикация. Там тоже задумались, а что такое читабельность кода и для чего она:


                      Если код легко править — он читабельный? А вот это, пожалуй, верное направление мысли. Меняются требования, добавляются функции, возникают ошибки — и в какой-то момент кому-то приходится править ваш код. А чтобы при этом не породить новые проблемы, разработчику нужно понимать, что конкретно он редактирует и как правки изменят поведение кода. Итак, мы нашли новое эвристическое правило: читабельный код должен легко редактироваться.


                      1. VolCh
                        14.03.2019 16:13

                        Выглядит как подмена понятий. Код может легко читаться, но сложно правиться. Ну, например,


                        if($cond) return true;

                        читается легче, чем


                        if ($cond) {
                          return true;
                        }

                        но правится сложнее.


                        1. flancer Автор
                          14.03.2019 16:26

                          Согласен, несколько неточно. Нужно говорить о модифицируемости кода, а не о читабельности.


              1. vlreshet
                14.03.2019 14:23

                Код разбивается на классы/процедуры/функции не потому, что человек не может прочесть 2К строк подряд
                Как раз таки именно потому! Прочитать код на 2к строк человек может, а вот держать в голове — ну, наверное, иногда это реально, но чаще всего — нет.
                код должен быть не легко читаем, а легко изменяем
                От этого выражения веет ламповым 2010-ым, где сайты представляли собой свалку файлов .php в корневой директории. Там было крайне легко что-то менять, и крайне сложно понять что, чёрт побери, происходит в целом.
                Кстати, удобнее читать код, который написан в одном файле, чем скакать от файла к файлу и обратно.
                Если использовать нормальный редактор кода, а ещё лучше IDE — то нет, ничем не удобнее. Даже наоборот.

                У меня к вам такой неловкий вопрос… Вы занимались профессиональной разработкой с использованием фреймворков вроде Laravel, Symfony, Phalcon, Zend, или весь ваш опыт основывается на работе с продуктами вроде Magento?


                1. flancer Автор
                  14.03.2019 15:09

                  Код разбивается на классы/процедуры/функции не потому, что человек не может прочесть 2К строк подряд
                  Как раз таки именно потому! Прочитать код на 2к строк человек может

                  не находите противоречия?


                  где сайты представляли собой свалку файлов .php в корневой директории.

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


                  У меня к вам такой неловкий вопрос…

                  У меня в профиле написаны языки, с которыми я имел дело. Magento использует Zend и Symfony для CLI. За Phalcon на могу ничего сказать — вообще не трогал. Laravel смотрел, но не более. Есть ещё неловкие вопросы?


                  1. vlreshet
                    14.03.2019 16:48

                    Код разбивается на классы/процедуры/функции не потому, что человек не может прочесть 2К строк подряд
                    Как раз таки именно потому! Прочитать код на 2к строк человек может

                    не находите противоречия?
                    Нет, потому что вы вырвали цитату из контекста. Прочитать 2к — человек может, а держать в голове и помнить контекст — нет. А код это не книга, которая просто бегло читается.

                    это ваше неверное представление о «ламповых» 2010-х, как неверно и ваше представление о том, что в свалке «легко менять» (возьмите на себя поддержку такой свалки хотя бы на пару лет).
                    Эээх, пятый год такую свалку поддерживаю… Причём свалку энтерпрайз уровня, не сайт-визитку. Поэтому я знаю о чём говорю. Подкинуть лишний «if» в помойку — почти всегда легче, чем современный подход (создания модели, экшна в контроллере, проведения роутинга, написания тестов). Но лучше ли?


      1. VolCh
        14.03.2019 12:25

        > Лично у меня return из начала-середины метода вызывает ассоциации только с goto.

        Именно! И альтернатива ранним возвратам как раз goto для меня. Но вот ваш пример для иллюстрации ранних возвратов не очень подходит — у вас все ветви равнозначные, кроме разве что первой. Ранний возврат или goto на возврат хорошо подходит когда есть несколько основных равнозначных ветвей и есть ветви для граничных случаев (в вашем пример, наверное, только отрицательные значения на неё подходят немного).


      1. Bluz
        14.03.2019 12:43

        Это потому что вы неправильно всё исправили =))

        function foo($baz)
        {
            if ($baz < 0) {
                return 0;
            } 
            
            if ($baz > 100 && $baz <= 1000) {
                return do_f1();
            } 
            
            if ($baz > 1000 && $baz <= 10000) {
                return  do_f2();
            } 
            
            if ($baz > 10000 && $baz <= 100000) {
                return  do_f3();
            } 
            
            return  do_f4();
        }


        Вот так мне нравится больше


        1. flancer Автор
          14.03.2019 12:47

          Я всего лишь переработал ваш код с $result'ами. Вы могли и его сделать чуть более красивым ;)


    1. aikixd
      14.03.2019 15:50
      -1

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


  1. megasuperlexa
    14.03.2019 11:48

    Чуть хуже то, что ты не можешь поставить безусловную точку останова (breakpoint) на ветке true или false. Придётся вместо одного клика в IDE поработать мышкой/клавиатурой чуть подольше, добавляя условную точку останова.

    Это что за IDE? в нормальных становишься курсором куда надо, жмёшь F9 и
    вуаля


  1. flancer Автор
    14.03.2019 11:52

    PhpStorm. По F9 открывается debug-меню:
    image


  1. kom93
    14.03.2019 11:53

    Что мешает установить breakpoint на любой метод в каскадной цепочке?


    1. flancer Автор
      14.03.2019 11:56

      Не знаю, у меня не получается. Научите. Я под IDEA работаю (PhpStorm).


      1. kom93
        14.03.2019 12:19

        В PhpStorm используйте функцию Smart Step Into.


        1. flancer Автор
          14.03.2019 12:22

          А вот за это — спасибо! Попробую.