Исповедь Битрикс хейтера

Что-то много развелось в последнее время статей про минусы битрикса, и их опровержений. Раз уж пошла такая пьянка, то и я добавлю свои 5 копеек.
В комментариях к статьям писали, что не хватает конкретики, примеров, более глубокого обзора.

Данная статья — попытка этот обзор написать. Хотя нет, это скорее пост ненависти и боли (может даже немного нытья). Это такой расширенный вариант поста про минусы от pistol. Я постараюсь описать большинство тех вещей, которые раздражают именно меня и моих коллег в Битриксе. Постараюсь собрать в одном посте все те минусы, которые доставляют ежедневно очень много боли. Под конец я постараюсь сделать выводы.

Кто я такой? Да в общем-то, обычный разработчик. Работаю с битриксом с ноября 2010 года (5.5 лет). Работаю только с битриксом, не сделал ни одного коммерческого проекта на других CMS, не использовал фреймворки в создании сайтов. По роду деятельности я занимаюсь в основном интернет-магазинами, их созданием, поддержкой и развитием.

TL;DR


Битрикс — УГ, не стоит лезть в этот омут без особой надобности.

Вместо вступления


Для начала я предлагаю вам провести мысленный эксперимент. Давайте попробуем взять двух backend-разработчиков примерно одного возраста и примерно с одинаковым стажем работы (допустим, 1 — 1.5 года), только чтобы один из них работал все это время с 1С-Битрикс, а другой — с Symfony(например). Можно легко сравнить, с каким набором технологий работал все это время один, а с каким — другой, и какой в итоге набор знаний они получили за это время.

В случае с Symfony разработчиком это будет: php5/7 + глубокое понимание ООП, общепринятые паттерны проектирования (MVC, DI, Factory, Repository как минимум), умение разрабатывать Unit тесты, использовать шаблонизаторы (минимум twig), ORM (с Doctrine), composer, git, стандарты PSR, опыт работы с консолью и написания консольных приложений, базовые навыки настройки веб-сервера.

В случае с 1С-Битрикс разработчиком это будет php5, html/css + javascript/jquery (из коробки шаблонизаторов нет, а битрикс сует логику в шаблоны, как ни крути, придется с этим возиться), возможно git (и это сильно зависит от компании, некоторые динозавры до сих пор пилят на продакшене через FTP), если повезет — немножно sql и… все?

Я понимаю, что это все очень индивидуально и очень большую роль может сыграть окружение человека. Но тут я говорю о том, к чему двигают разработчика системы из коробки. В большинстве случаев, Битрикс разработчик очень сильно уступает в навыках по сравнению с разработчиками под другие фреймворки/CMS — и это неоспоримый факт. А все потому, что Битрикс изначально дает слишком много свободы при отсутствии внятной архитектуры, документации, и правильных решений, тогда как Symfony предлагает все необходимое.

Лишь однажды к нам в компанию пришел опытный человек не из мира 1С-Битрикс (в регионе) и он был на голову сильнее своих коллег с тем же стажем просто за счет того, что ранее ему поставили мозги на правильные рельсы.
Я и сам такой. Мне, к сожалению, с самого начала пустили пыль в глаза той же маркетинговой чепухой, да и попал я в не очень хорошее окружение. Я сам понимаю и чувствую, что мои коллеги с аналогичным стажем работы, но в том же Symfony, имеют больший кругозор, и это очень сильный побочный эффект от битрикса.
Это все наводит на мысли о том, что если ты хочешь развиваться в мире веб-разработки, то в качестве основы нужно выбирать уж точно не битрикс.

Сравнивая двух разработчиков, я хочу обратить внимание на те рамки, в которые загоняет система, и на ту свободу, которую она предоставляет. Что Битрикс, что Symfony — они оба дают почти безграничную гибкость, и в принципе на каждой из них можно создать продукт абсолютно любой сложности. Однако система должна помогать разработчику в решении проблем, вместо того, чтобы вставлять палки в колеса. И тут Битрикс очень сильно проигрывает.

Маркетинг


Сразу хочу сказать немного слов об этом, т.к. это основная составляющая успеха Битрикса.
Можно сказать, что духом маркетинга пропитан весь Битрикс, даже документация для разработчиков. Даже там они пишут о том, что их продукт «настолько крутой, что его ценят и уважают все наши партнеры»(пруф, блок «Структура»). В битриксе работают хорошие маркетологи, которые грамотно умеют преподнести свой продукт. Раз в полгода они устраивают конференции для партнеров, где рассказывают о том, что было сделано и о том, какие у них планы. Как показывает практика, никогда эти планы не сбываются в срок и очень часто релизы либо неполные, либо с кучей ошибок.
В качестве примера — нашумевший рефакторинг модуля sale, релиз которого откладывали больше года, и даже самую последнюю дату релиза (23 декабря 2015 года) провалили на 3 месяца, и выпустили новый магазин и БУС (Битрикс ред. «Управление Сайтом») 16 версии только в конце марта 2016го. Но в результате после обновления пользователи не только не получили новых фич. Пользователи получили в большинстве случаев неработоспособный магазин, и горку нового недокументированного кода в придачу.
Новым инструментам дают такие громкие названия, которые у всех на слуху: Композитный сайт — ускорение x100; Highload-блоки; Bitrix BigData. На самом деле за этими словами скрываются вполне обыденные вещи, которые не соответствуют своему имени.
И такой подход прослеживается везде, к сожалению. Снаружи продукт выглядит как конфетка, которую купил, поставил и пользуешься. Но если с битриксом сделать шаг в сторону от стандартной поставки — все, поддержание функциональности при обновлениях превращается в ад.
Впрочем, обо всем по порядку, тема маркетинга еще будет всплывать в этом посте, скорее всего, не раз.

Архитектура


На протяжении десятка лет Битрикс отчаянно загонял сам себя в тупик. Каждая новая фича в продукте выходила в соответствии с интересами бизнеса, без должной проработки с технической точки зрения. И, естественно, все это росло как снежный ком.
Если вдуматься, то в Битриксе нет архитектуры, как таковой. Нет даже общепринятых сформулированных правил, которые позволили бы следовать этой архитектуре. В курсе разработчиков, в разделе Архитектура продукта, сказано, что битрикс следует архитектуре MVC и приводит схему:

Битрикс MVC

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

M — Model, или API


Мне сложно судить об API системы как о модели. Да, API предоставляет интерфейс доступа к данным и позволяет ими манипулировать. Но API битрикса позволяет работать не только с данными, но и с шаблонами, да и с пользовательскими запросами тоже. Ну да ладно… это лишь мое мнение.
На данный момент в Битриксе есть 2 варианта API. Условно можно разделить их на старое и новое. Новое API называется D7 (честно — не помню почему, но Rizhikov рассказывал об этом на одной из партнерских конференций).

Старое API — это собрание антипаттернов, ужасных примеров плохого кода. В Битриксе всегда считалось нормальным вызывать нестатические методы статически, и наоборот, требовать состояния тогда, когда это неуместно. Например, всем известный CIBlockElement::GetList — пожалуй, один из самых часто-используемых методов при разработке. Его реализация содержит более 500 строк кода, использует глобалки, строит ужасающие, колоссальные запросы, и содержит нереальный, просто нечитабельный недокументированный код.
Смотрим
function GetList($arOrder=array("SORT"=>"ASC"), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
    {
        /*
        Filter combinations:
        CHECK_PERMISSIONS="N" - check permissions of the current user to the infoblock
            MIN_PERMISSION="R" - when permissions check, then minimal access level
        SHOW_HISTORY="N" - add history items to list
            SHOW_NEW="N" - if not add history items, then add new, but not published elements
        */
        global $DB, $USER;
        $MAX_LOCK = intval(COption::GetOptionString("workflow","MAX_LOCK_TIME","60"));
        $uid = is_object($USER)? intval($USER->GetID()): 0;

        $formatActiveDates = CPageOption::GetOptionString("iblock", "FORMAT_ACTIVE_DATES", "-") != "-";
        $shortFormatActiveDates = CPageOption::GetOptionString("iblock", "FORMAT_ACTIVE_DATES", "SHORT");

        $arIblockElementFields = array(
                "ID"=>"BE.ID",
                "TIMESTAMP_X"=>$DB->DateToCharFunction("BE.TIMESTAMP_X"),
                "TIMESTAMP_X_UNIX"=>'UNIX_TIMESTAMP(BE.TIMESTAMP_X)',
                "MODIFIED_BY"=>"BE.MODIFIED_BY",
                "DATE_CREATE"=>$DB->DateToCharFunction("BE.DATE_CREATE"),
                "DATE_CREATE_UNIX"=>'UNIX_TIMESTAMP(BE.DATE_CREATE)',
                "CREATED_BY"=>"BE.CREATED_BY",
                "IBLOCK_ID"=>"BE.IBLOCK_ID",
                "IBLOCK_SECTION_ID"=>"BE.IBLOCK_SECTION_ID",
                "ACTIVE"=>"BE.ACTIVE",
                "ACTIVE_FROM"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_FROM", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_FROM)>0, ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "SHORT").")"
                        ),
                "ACTIVE_TO"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_TO", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_TO)>0, ".$DB->DateToCharFunction("BE.ACTIVE_TO", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_TO", "SHORT").")"
                        ),
                "DATE_ACTIVE_FROM"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_FROM", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_FROM)>0, ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_FROM", "SHORT").")"
                        ),
                "DATE_ACTIVE_TO"=>(
                        $formatActiveDates
                        ?
                            $DB->DateToCharFunction("BE.ACTIVE_TO", $shortFormatActiveDates)
                        :
                            "IF(EXTRACT(HOUR_SECOND FROM BE.ACTIVE_TO)>0, ".$DB->DateToCharFunction("BE.ACTIVE_TO", "FULL").", ".$DB->DateToCharFunction("BE.ACTIVE_TO", "SHORT").")"
                        ),
                "SORT"=>"BE.SORT",
                "NAME"=>"BE.NAME",
                "PREVIEW_PICTURE"=>"BE.PREVIEW_PICTURE",
                "PREVIEW_TEXT"=>"BE.PREVIEW_TEXT",
                "PREVIEW_TEXT_TYPE"=>"BE.PREVIEW_TEXT_TYPE",
                "DETAIL_PICTURE"=>"BE.DETAIL_PICTURE",
                "DETAIL_TEXT"=>"BE.DETAIL_TEXT",
                "DETAIL_TEXT_TYPE"=>"BE.DETAIL_TEXT_TYPE",
                "SEARCHABLE_CONTENT"=>"BE.SEARCHABLE_CONTENT",
                "WF_STATUS_ID"=>"BE.WF_STATUS_ID",
                "WF_PARENT_ELEMENT_ID"=>"BE.WF_PARENT_ELEMENT_ID",
                "WF_LAST_HISTORY_ID"=>"BE.WF_LAST_HISTORY_ID",
                "WF_NEW"=>"BE.WF_NEW",
                "LOCK_STATUS"=>"if (BE.WF_DATE_LOCK is null, 'green', if(DATE_ADD(BE.WF_DATE_LOCK, interval ".$MAX_LOCK." MINUTE)<now(), 'green', if(BE.WF_LOCKED_BY=".$uid.", 'yellow', 'red')))",
                "WF_LOCKED_BY"=>"BE.WF_LOCKED_BY",
                "WF_DATE_LOCK"=>$DB->DateToCharFunction("BE.WF_DATE_LOCK"),
                "WF_COMMENTS"=>"BE.WF_COMMENTS",
                "IN_SECTIONS"=>"BE.IN_SECTIONS",
                "SHOW_COUNTER"=>"BE.SHOW_COUNTER",
                "SHOW_COUNTER_START"=>$DB->DateToCharFunction("BE.SHOW_COUNTER_START"),
                "CODE"=>"BE.CODE",
                "TAGS"=>"BE.TAGS",
                "XML_ID"=>"BE.XML_ID",
                "EXTERNAL_ID"=>"BE.XML_ID",
                "TMP_ID"=>"BE.TMP_ID",
                "USER_NAME"=>"concat('(',U.LOGIN,') ',ifnull(U.NAME,''),' ',ifnull(U.LAST_NAME,''))",
                "LOCKED_USER_NAME"=>"concat('(',UL.LOGIN,') ',ifnull(UL.NAME,''),' ',ifnull(UL.LAST_NAME,''))",
                "CREATED_USER_NAME"=>"concat('(',UC.LOGIN,') ',ifnull(UC.NAME,''),' ',ifnull(UC.LAST_NAME,''))",
                "LANG_DIR"=>"L.DIR",
                "LID"=>"B.LID",
                "IBLOCK_TYPE_ID"=>"B.IBLOCK_TYPE_ID",
                "IBLOCK_CODE"=>"B.CODE",
                "IBLOCK_NAME"=>"B.NAME",
                "IBLOCK_EXTERNAL_ID"=>"B.XML_ID",
                "DETAIL_PAGE_URL"=>"B.DETAIL_PAGE_URL",
                "LIST_PAGE_URL"=>"B.LIST_PAGE_URL",
                "CANONICAL_PAGE_URL"=>"B.CANONICAL_PAGE_URL",
                "CREATED_DATE"=>$DB->DateFormatToDB("YYYY.MM.DD", "BE.DATE_CREATE"),
                "BP_PUBLISHED"=>"if(BE.WF_STATUS_ID = 1, 'Y', 'N')",
            );
        unset($shortFormatActiveDates);
        unset($formatActiveDates);

        $bDistinct = false;

        CIBlockElement::PrepareGetList(
                $arIblockElementFields,
                $arJoinProps,
                $bOnlyCount,
                $bDistinct,

                $arSelectFields,
                $sSelect,
                $arAddSelectFields,

                $arFilter,
                $sWhere,
                $sSectionWhere,
                $arAddWhereFields,

                $arGroupBy,
                $sGroupBy,

                $arOrder,
                $arSqlOrder,
                $arAddOrderByFields,

                $arIBlockFilter,
                $arIBlockMultProps,
                $arIBlockConvProps,
                $arIBlockAllProps,
                $arIBlockNumProps,
                $arIBlockLongProps
            );

        $arFilterIBlocks = isset($arFilter["IBLOCK_ID"])? array($arFilter["IBLOCK_ID"]): array();
        //******************FROM PART********************************************
        $sFrom = "";
        foreach($arJoinProps["FPS"] as $iblock_id => $iPropCnt)
        {
            $sFrom .= "\t\t\tINNER JOIN b_iblock_element_prop_s".$iblock_id." FPS".$iPropCnt." ON FPS".$iPropCnt.".IBLOCK_ELEMENT_ID = BE.ID\n";
            $arFilterIBlocks[$iblock_id] = $iblock_id;
        }

        foreach($arJoinProps["FP"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN b_iblock_property FP".$i." ON FP".$i.".IBLOCK_ID = B.ID AND ".
                    (
                        IntVal($propID)>0?
                        " FP".$i.".ID=".IntVal($propID)."\n":
                        " FP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );
            else
                $sFrom .= "\t\t\tLEFT JOIN b_iblock_property FP".$i." ON FP".$i.".IBLOCK_ID = B.ID AND ".
                    (
                        IntVal($propID)>0?
                        " FP".$i.".ID=".IntVal($propID)."\n":
                        " FP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["FPV"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            if($db_prop["MULTIPLE"]=="Y")
                $bDistinct = true;

            if($db_prop["VERSION"]==2)
                $strTable = "b_iblock_element_prop_m".$db_prop["IBLOCK_ID"];
            else
                $strTable = "b_iblock_element_property";

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN ".$strTable." FPV".$i." ON FPV".$i.".IBLOCK_PROPERTY_ID = FP".$db_prop["JOIN"].".ID AND FPV".$i.".IBLOCK_ELEMENT_ID = BE.ID\n";
            else
                $sFrom .= "\t\t\tLEFT JOIN ".$strTable." FPV".$i." ON FPV".$i.".IBLOCK_PROPERTY_ID = FP".$db_prop["JOIN"].".ID AND FPV".$i.".IBLOCK_ELEMENT_ID = BE.ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["FPEN"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            if($db_prop["VERSION"] == 2 && $db_prop["MULTIPLE"] == "N")
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND FPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = FPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND FPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = FPEN".$i.".ID\n";
            }
            else
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = FPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND FPV".$db_prop["JOIN"].".VALUE_ENUM = FPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum FPEN".$i." ON FPEN".$i.".PROPERTY_ID = FPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND FPV".$db_prop["JOIN"].".VALUE_ENUM = FPEN".$i.".ID\n";
            }

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];

            $sFrom .= "\t\t\tLEFT JOIN b_iblock_element BE".$i." ON BE".$i.".ID = ".
                (
                    $db_prop["VERSION"]==2 && $db_prop["MULTIPLE"]=="N"?
                    "FPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]
                    :"FPV".$db_prop["JOIN"].".VALUE_NUM"
                ).
                (
                    $arFilter["SHOW_HISTORY"] != "Y"?
                    " AND ((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)".($arFilter["SHOW_NEW"]=="Y"? " OR BE.WF_NEW='Y'": "").")":
                    ""
                )."\n";

            if($db_prop["bJoinIBlock"])
                $sFrom .= "\t\t\tLEFT JOIN b_iblock B".$i." ON B".$i.".ID = BE".$i.".IBLOCK_ID\n";

            if($db_prop["bJoinSection"])
                $sFrom .= "\t\t\tLEFT JOIN b_iblock_section BS".$i." ON BS".$i.".ID = BE".$i.".IBLOCK_SECTION_ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FPS"] as $iblock_id => $db_prop)
        {
            $sFrom .= "\t\t\tLEFT JOIN b_iblock_element_prop_s".$iblock_id." JFPS".$db_prop["CNT"]." ON JFPS".$db_prop["CNT"].".IBLOCK_ELEMENT_ID = BE".$db_prop["JOIN"].".ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FP"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];
            list($propID, $link) = explode("~", $propID, 2);

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN b_iblock_property JFP".$i." ON JFP".$i.".IBLOCK_ID = BE".$db_prop["JOIN"].".IBLOCK_ID AND ".
                    (
                        IntVal($propID)>0?
                        " JFP".$i.".ID=".IntVal($propID)."\n":
                        " JFP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );
            else
                $sFrom .= "\t\t\tLEFT JOIN b_iblock_property JFP".$i." ON JFP".$i.".IBLOCK_ID = BE".$db_prop["JOIN"].".IBLOCK_ID AND ".
                    (
                        IntVal($propID)>0?
                        " JFP".$i.".ID=".IntVal($propID)."\n":
                        " JFP".$i.".CODE='".$DB->ForSQL($propID, 200)."'\n"
                    );

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FPV"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];
            list($propID, $link) = explode("~", $propID, 2);

            if($db_prop["MULTIPLE"]=="Y")
                $bDistinct = true;

            if($db_prop["VERSION"]==2)
                $strTable = "b_iblock_element_prop_m".$db_prop["IBLOCK_ID"];
            else
                $strTable = "b_iblock_element_property";

            if($db_prop["bFullJoin"])
                $sFrom .= "\t\t\tINNER JOIN ".$strTable." JFPV".$i." ON JFPV".$i.".IBLOCK_PROPERTY_ID = JFP".$db_prop["JOIN"].".ID AND JFPV".$i.".IBLOCK_ELEMENT_ID = BE".$db_prop["BE_JOIN"].".ID\n";
            else
                $sFrom .= "\t\t\tLEFT JOIN ".$strTable." JFPV".$i." ON JFPV".$i.".IBLOCK_PROPERTY_ID = JFP".$db_prop["JOIN"].".ID AND JFPV".$i.".IBLOCK_ELEMENT_ID = BE".$db_prop["BE_JOIN"].".ID\n";

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        foreach($arJoinProps["BE_FPEN"] as $propID => $db_prop)
        {
            $i = $db_prop["CNT"];
            list($propID, $link) = explode("~", $propID, 2);

            if($db_prop["VERSION"] == 2 && $db_prop["MULTIPLE"] == "N")
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND JFPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = JFPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = ".$db_prop["ORIG_ID"]." AND JFPS".$db_prop["JOIN"].".PROPERTY_".$db_prop["ORIG_ID"]." = JFPEN".$i.".ID\n";
            }
            else
            {
                if($db_prop["bFullJoin"])
                    $sFrom .= "\t\t\tINNER JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = JFPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND JFPV".$db_prop["JOIN"].".VALUE_ENUM = JFPEN".$i.".ID\n";
                else
                    $sFrom .= "\t\t\tLEFT JOIN b_iblock_property_enum JFPEN".$i." ON JFPEN".$i.".PROPERTY_ID = JFPV".$db_prop["JOIN"].".IBLOCK_PROPERTY_ID AND JFPV".$db_prop["JOIN"].".VALUE_ENUM = JFPEN".$i.".ID\n";
            }

            if($db_prop["IBLOCK_ID"])
                $arFilterIBlocks[$db_prop["IBLOCK_ID"]] = $db_prop["IBLOCK_ID"];
        }

        if(strlen($arJoinProps["BES"]))
        {
            $sFrom .= "\t\t\t".$arJoinProps["BES"]."\n";
        }

        if(strlen($arJoinProps["FC"]))
        {
            $sFrom .= "\t\t\t".$arJoinProps["FC"]."\n";
            $bDistinct = $bDistinct || (isset($arJoinProps["FC_DISTINCT"]) && $arJoinProps["FC_DISTINCT"] == "Y");
        }

        if($arJoinProps["RV"])
            $sFrom .= "\t\t\tLEFT JOIN b_rating_voting RV ON RV.ENTITY_TYPE_ID = 'IBLOCK_ELEMENT' AND RV.ENTITY_ID = BE.ID\n";
        if($arJoinProps["RVU"])
            $sFrom .= "\t\t\tLEFT JOIN b_rating_vote RVU ON RVU.ENTITY_TYPE_ID = 'IBLOCK_ELEMENT' AND RVU.ENTITY_ID = BE.ID AND RVU.USER_ID = ".$uid."\n";
        if($arJoinProps["RVV"])
            $sFrom .= "\t\t\t".($arJoinProps["RVV"]["bFullJoin"]? "INNER": "LEFT")." JOIN b_rating_vote RVV ON RVV.ENTITY_TYPE_ID = 'IBLOCK_ELEMENT' AND RVV.ENTITY_ID = BE.ID\n";

        //******************END OF FROM PART********************************************

        $bCatalogSort = false;
        if(count($arAddSelectFields)>0 || count($arAddWhereFields)>0 || count($arAddOrderByFields)>0)
        {
            if(CModule::IncludeModule("catalog"))
            {
                $res_catalog = CCatalogProduct::GetQueryBuildArrays($arAddOrderByFields, $arAddWhereFields, $arAddSelectFields);
                if(
                    $sGroupBy==""
                    && !$bOnlyCount
                    && !(is_object($this) && isset($this->strField))
                )
                    $sSelect .= $res_catalog["SELECT"]." ";
                $sFrom .= str_replace("LEFT JOIN", "\n\t\t\tLEFT JOIN", $res_catalog["FROM"])."\n";
                //$sWhere .= $res_catalog["WHERE"]." "; moved to MkFilter
                if(is_array($res_catalog["ORDER"]) && count($res_catalog["ORDER"]))
                {
                    $bCatalogSort = true;
                    foreach($res_catalog["ORDER"] as $i=>$val)
                        $arSqlOrder[$i] = $val;
                }
            }
        }

        $i = array_search("CREATED_BY_FORMATTED", $arSelectFields);
        if ($i !== false)
        {
            if (
                $sSelect
                && $sGroupBy==""
                && !$bOnlyCount
                && !(is_object($this) && isset($this->strField))
            )
            {
                $sSelect .= ",UC.NAME UC_NAME, UC.LAST_NAME UC_LAST_NAME, UC.SECOND_NAME UC_SECOND_NAME, UC.EMAIL UC_EMAIL, UC.ID UC_ID, UC.LOGIN UC_LOGIN";
            }
            else
            {
                unset($arSelectFields[$i]);
            }
        }

        $sOrderBy = "";
        foreach($arSqlOrder as $i=>$val)
        {
            if(strlen($val))
            {
                if($sOrderBy=="")
                    $sOrderBy = " ORDER BY ";
                else
                    $sOrderBy .= ",";

                $sOrderBy .= $val." ";
            }
        }

        $sSelect = trim($sSelect, ", \t\n\r");
        if(strlen($sSelect) <= 0)
            $sSelect = "0 as NOP ";

        $bDistinct = $bDistinct || (isset($arFilter["INCLUDE_SUBSECTIONS"]) && $arFilter["INCLUDE_SUBSECTIONS"] == "Y");

        if($bDistinct)
            $sSelect = str_replace("%%_DISTINCT_%%", "DISTINCT", $sSelect);
        else
            $sSelect = str_replace("%%_DISTINCT_%%", "", $sSelect);

        $sFrom = "
            b_iblock B
            INNER JOIN b_lang L ON B.LID=L.LID
            INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID
            ".ltrim($sFrom, "\t\n")
            .(in_array("USER_NAME", $arSelectFields)? "\t\t\tLEFT JOIN b_user U ON U.ID=BE.MODIFIED_BY\n": "")
            .(in_array("LOCKED_USER_NAME", $arSelectFields)? "\t\t\tLEFT JOIN b_user UL ON UL.ID=BE.WF_LOCKED_BY\n": "")
            .(in_array("CREATED_USER_NAME", $arSelectFields) || in_array("CREATED_BY_FORMATTED", $arSelectFields)? "\t\t\tLEFT JOIN b_user UC ON UC.ID=BE.CREATED_BY\n": "")."
        ";

        $strSql = "
            FROM ".$sFrom."
            WHERE 1=1 "
            .$sWhere."
            ".$sGroupBy."
        ";

        if(isset($this) && is_object($this) && isset($this->strField))
        {
            $this->sFrom = $sFrom;
            $this->sWhere = $sWhere;
            return "SELECT ".$sSelect.$strSql;
        }

        if($bOnlyCount)
        {
            $res = $DB->Query("SELECT ".$sSelect.$strSql, false, "FILE: ".__FILE__."<br> LINE: ".__LINE__);
            $res = $res->Fetch();
            return $res["CNT"];
        }

        if(is_array($arNavStartParams))
        {
            $nTopCount = intval($arNavStartParams["nTopCount"]);
            $nElementID = intval($arNavStartParams["nElementID"]);

            if($nTopCount > 0)
            {
                $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy." LIMIT ".$nTopCount;
                $res = $DB->Query($strSql);
            }
            elseif(
                $nElementID > 0
                && $sGroupBy == ""
                && $sOrderBy != ""
                && strpos($sSelect, "BE.ID") !== false
                && !$bCatalogSort
            )
            {
                $nPageSize = intval($arNavStartParams["nPageSize"]);

                if($nPageSize > 0)
                {
                    $DB->Query("SET @rank_e=0");

                    $DB->Query("SET @rank_r=0");
                    $DB->Query("
                        SELECT
                            ".$sSelect."
                            ,@rank_r:=@rank_r+1 AS rank1
                            ,if (BE.ID = ".$nElementID.", @rank_e:=@rank_r, null) rank2
                        ".$strSql.$sOrderBy."
                    ");

                    $DB->Query("SET @rank_r=0");
                    $res = $DB->Query("
                        SELECT *
                        FROM (
                            SELECT
                                ".$sSelect."
                                ,@rank_r:=@rank_r+1 AS RANK
                            ".$strSql.$sOrderBy."
                            LIMIT 18446744073709551615
                        ) el0
                        WHERE el0.RANK between @rank_e-$nPageSize and @rank_e+$nPageSize
                    ");
                }
                else
                {
                    $DB->Query("SET @rank=0");
                    $res = $DB->Query("
                        SELECT *
                        FROM (
                            SELECT
                                ".$sSelect."
                                ,@rank:=@rank+1 AS RANK
                            ".$strSql.$sOrderBy."
                            LIMIT 18446744073709551615
                        ) el0
                        WHERE el0.ID = ".$nElementID."
                    ");
                }
            }
            else
            {
                if($sGroupBy == "")
                {
                    $res_cnt = $DB->Query("SELECT COUNT(".($bDistinct? "DISTINCT BE.ID": "'x'").") as C ".$strSql);
                    $res_cnt = $res_cnt->Fetch();
                    $cnt = $res_cnt["C"];
                }
                else
                {
                    $res_cnt = $DB->Query("SELECT 'x' ".$strSql);
                    $cnt = $res_cnt->SelectedRowsCount();
                }

                $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy;
                $res = new CDBResult();
                $res->NavQuery($strSql, $cnt, $arNavStartParams);
            }
        }
        else//if(is_array($arNavStartParams))
        {
            $strSql = "SELECT ".$sSelect.$strSql.$sOrderBy;
            $res = $DB->Query($strSql, false, "FILE: ".__FILE__."<br> LINE: ".__LINE__);
        }

        $res = new CIBlockResult($res);
        $res->SetIBlockTag($arFilterIBlocks);
        $res->arIBlockMultProps = $arIBlockMultProps;
        $res->arIBlockConvProps = $arIBlockConvProps;
        $res->arIBlockAllProps  = $arIBlockAllProps;
        $res->arIBlockNumProps = $arIBlockNumProps;
        $res->arIBlockLongProps = $arIBlockLongProps;

        return $res;
    }



Как можно догадаться, этот метод получает из БД список элементов инфоблока, и для получения списка не требуется создания экземпляра класса CIBlockElement. Однако, чтобы добавить элемент инфоблока, обязательно нужно состояние, и только для того, чтобы записать инфо о последней произошедшей ошибке в публичное свойство класса.

В старом API очень активно используются такие глобальные переменные, как $APPLICATION, $USER, $DB. Они являются экземплярами определенных классов, и в документации раньше гордо звались синглтонами, правда сейчас я не нашел уже этих слов.
Для того, чтобы сгенерировать ошибку, например, в обработчиках событий, нужно воспользоваться методом $APPLICATION->ThrowException(), который на самом деле исключения не бросает.
    public function ThrowException($msg, $id = false)
    {
        $this->ResetException();
        if(is_object($msg) && (is_subclass_of($msg, 'CApplicationException') || (strtolower(get_class($msg))=='capplicationexception')))
            $this->LAST_ERROR = $msg;
        else
            $this->LAST_ERROR = new CApplicationException($msg, $id);
    }


И да — вся эта красота до сих пор используется при разработке новых проектов, т.к. D7 пока еще не поддерживает всех возможностей старого API. Тот же модуль инфоблоков до сих пор позволяет выполнять только выборку сущностей, причем не целиком. Создать новый элемент, или обновить существующий с помощью нового API пока нельзя.

Новое API уже несколько отличается от старого. Во-первых, весь код из нового ядра разложен по неймспейсам, где прослеживается четкая зависимость от модуля. Например, аналог CIBlockElement::GetList из нового ядра — Bitrix\Iblock\ElementTable::getList, где корневое пространство имен — это имя вендора, а следующее — имя модуля. Для того, чтобы это работало, в битриксе написали свой автозагрузчик \Bitrix\Main\Loader::autoLoad, который совсем не совместим с PSR-0/4.

Собственно, код автозагрузчика в виде одной функции
        public static function autoLoad($className)
	{
		$file = ltrim($className, "\\");    // fix web env
		$file = strtr($file, static::ALPHA_UPPER, static::ALPHA_LOWER);

		static $documentRoot = null;
		if ($documentRoot === null)
			$documentRoot = static::getDocumentRoot();

		if (isset(self::$arAutoLoadClasses[$file]))
		{
			$pathInfo = self::$arAutoLoadClasses[$file];
			if ($pathInfo["module"] != "")
			{
				$m = $pathInfo["module"];
				$h = isset(self::$arLoadedModulesHolders[$m]) ? self::$arLoadedModulesHolders[$m] : 'bitrix';
				include_once($documentRoot."/".$h."/modules/".$m."/" .$pathInfo["file"]);
			}
			else
			{
				require_once($documentRoot.$pathInfo["file"]);
			}
			return;
		}

		if (preg_match("#[^\\\\/a-zA-Z0-9_]#", $file))
			return;

		if (substr($file, -5) == "table")
			$file = substr($file, 0, -5);

		$file = str_replace('\\', '/', $file);
		$arFile = explode("/", $file);

		if ($arFile[0] === "bitrix")
		{
			array_shift($arFile);

			if (empty($arFile))
				return;

			$module = array_shift($arFile);
			if ($module == null || empty($arFile))
				return;
		}
		else
		{
			$module1 = array_shift($arFile);
			$module2 = array_shift($arFile);
			if ($module1 == null || $module2 == null || empty($arFile))
				return;

			$module = $module1.".".$module2;
		}

		if (!isset(self::$arLoadedModulesHolders[$module]))
			return;

		$filePath = $documentRoot."/".self::$arLoadedModulesHolders[$module]."/modules/".$module."/lib/".implode("/", $arFile).".php";

		if (file_exists($filePath))
			require_once($filePath);
	}



В новом API прослеживается большая любовь к Singleton:
  • \Bitrix\Main\Application::getInstance — инстанс приложения
  • \Bitrix\Main\Config\Configuration::getInstance — инстанс класса для управления конфигами
  • \Bitrix\Main\Page\Asset::getInstance — инстанс Asset-менеджера
  • \Bitrix\Main\EventManager::getInstance — менеджер событий


Возможно, это все в будущем обрастет своим ServiceLayer'ом (есть некий \Bitrix\Main\ServiceManager в новом ядре, который пока не используется и не документирован). Но надежды пока мало.

ORM — еще одно из нововведений D7, и это уже что-то, что может претендовать на звание настоящей модели! Отличить класс сущности ORM от любого другого класса можно по его имени. Класс сущности всегда должен заканчиваться на Table (ElementTable, SectionTable, OrderTable и т.д.). Причем, парадокс, имя файла с классом ORM сущности не должно заканчиваться на Table. К примеру, для ElementTable мы должны создать файл element.php. Ниже на скрине представлено содержимое директории lib (только в этой директории работает автозагрузка D7) модуля iblock. Попробуйте на глазок определить, что есть сущности ORM, а что — обычные классы с бизнес-логикой.

Структура модуля iblock

ORM, по большому счету, пока что не представляет из себя ничего особенного. Оно позволяет описывать таблицы БД в виде классов и позволяет выполнять запросы к этим таблицам, связывать их между собой. Никакого ActiveRecord и Repository нет и не предвидится.

Пример типичного класса сущности ORM для элемента инфоблока
<?
namespace Bitrix\Iblock;

use Bitrix\Main;
use Bitrix\Main\Localization\Loc;

Loc::loadMessages(__FILE__);

/**
 * Class ElementTable
 *
 * Fields:
 * <ul>
 * <li> ID int mandatory
 * <li> TIMESTAMP_X datetime optional
 * <li> MODIFIED_BY int optional
 * <li> DATE_CREATE datetime optional
 * <li> CREATED_BY int optional
 * <li> IBLOCK_ID int mandatory
 * <li> IBLOCK_SECTION_ID int optional
 * <li> ACTIVE bool optional default 'Y'
 * <li> ACTIVE_FROM datetime optional
 * <li> ACTIVE_TO datetime optional
 * <li> SORT int optional default 500
 * <li> NAME string(255) mandatory
 * <li> PREVIEW_PICTURE int optional
 * <li> PREVIEW_TEXT string optional
 * <li> PREVIEW_TEXT_TYPE enum ('text', 'html') optional default 'text'
 * <li> DETAIL_PICTURE int optional
 * <li> DETAIL_TEXT string optional
 * <li> DETAIL_TEXT_TYPE enum ('text', 'html') optional default 'text'
 * <li> SEARCHABLE_CONTENT string optional
 * <li> WF_STATUS_ID int optional default 1
 * <li> WF_PARENT_ELEMENT_ID int optional
 * <li> WF_NEW enum ('N', 'Y') optional
 * <li> WF_LOCKED_BY int optional
 * <li> WF_DATE_LOCK datetime optional
 * <li> WF_COMMENTS string optional
 * <li> IN_SECTIONS bool optional default 'N'
 * <li> XML_ID string(255) optional
 * <li> CODE string(255) optional
 * <li> TAGS string(255) optional
 * <li> TMP_ID string(40) optional
 * <li> WF_LAST_HISTORY_ID int optional
 * <li> SHOW_COUNTER int optional
 * <li> SHOW_COUNTER_START datetime optional
 * <li> PREVIEW_PICTURE_FILE reference to {@link \Bitrix\File\FileTable}
 * <li> DETAIL_PICTURE_FILE reference to {@link \Bitrix\File\FileTable}
 * <li> IBLOCK reference to {@link \Bitrix\Iblock\IblockTable}
 * <li> WF_PARENT_ELEMENT reference to {@link \Bitrix\Iblock\IblockElementTable}
 * <li> IBLOCK_SECTION reference to {@link \Bitrix\Iblock\IblockSectionTable}
 * <li> MODIFIED_BY_USER reference to {@link \Bitrix\User\UserTable}
 * <li> CREATED_BY_USER reference to {@link \Bitrix\User\UserTable}
 * <li> WF_LOCKED_BY_USER reference to {@link \Bitrix\User\UserTable}
 * </ul>
 *
 * @package Bitrix\Iblock
 **/

class ElementTable extends Main\Entity\DataManager
{
	const TYPE_TEXT = 'text';
	const TYPE_HTML = 'html';

	/**
	 * Returns DB table name for entity.
	 *
	 * @return string
	 */
	public static function getTableName()
	{
		return 'b_iblock_element';
	}

	/**
	 * Returns entity map definition.
	 *
	 * @return array
	 */
	public static function getMap()
	{
		return array(
			'ID' => new Main\Entity\IntegerField('ID', array(
				'primary' => true,
				'autocomplete' => true,
				'title' => Loc::getMessage('ELEMENT_ENTITY_ID_FIELD'),
			)),
			'TIMESTAMP_X' => new Main\Entity\DatetimeField('TIMESTAMP_X', array(
				'default_value' => new Main\Type\DateTime(),
				'title' => Loc::getMessage('ELEMENT_ENTITY_TIMESTAMP_X_FIELD'),
			)),
			'MODIFIED_BY' => new Main\Entity\IntegerField('MODIFIED_BY', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_MODIFIED_BY_FIELD'),
			)),
			'DATE_CREATE' => new Main\Entity\DatetimeField('DATE_CREATE', array(
				'default_value' => new Main\Type\DateTime(),
				'title' => Loc::getMessage('ELEMENT_ENTITY_DATE_CREATE_FIELD'),
			)),
			'CREATED_BY' => new Main\Entity\IntegerField('CREATED_BY', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_CREATED_BY_FIELD'),
			)),
			'IBLOCK_ID' => new Main\Entity\IntegerField('IBLOCK_ID', array(
				'required' => true,
				'title' => Loc::getMessage('ELEMENT_ENTITY_IBLOCK_ID_FIELD'),
			)),
			'IBLOCK_SECTION_ID' => new Main\Entity\IntegerField('IBLOCK_SECTION_ID', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_IBLOCK_SECTION_ID_FIELD'),
			)),
			'ACTIVE' => new Main\Entity\BooleanField('ACTIVE', array(
				'values' => array('N', 'Y'),
				'default_value' => 'Y',
				'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_FIELD'),
			)),
			'ACTIVE_FROM' => new Main\Entity\DatetimeField('ACTIVE_FROM', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_FROM_FIELD'),
			)),
			'ACTIVE_TO' => new Main\Entity\DatetimeField('ACTIVE_TO', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_TO_FIELD'),
			)),
			'SORT' => new Main\Entity\IntegerField('SORT', array(
				'default_value' => 500,
				'title' => Loc::getMessage('ELEMENT_ENTITY_SORT_FIELD'),
			)),
			'NAME' => new Main\Entity\StringField('NAME', array(
				'required' => true,
				'validation' => array(__CLASS__, 'validateName'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_NAME_FIELD'),
			)),
			'PREVIEW_PICTURE' => new Main\Entity\IntegerField('PREVIEW_PICTURE', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_PICTURE_FIELD'),
			)),
			'PREVIEW_TEXT' => new Main\Entity\TextField('PREVIEW_TEXT', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_TEXT_FIELD'),
			)),
			'PREVIEW_TEXT_TYPE' => new Main\Entity\EnumField('PREVIEW_TEXT_TYPE', array(
				'values' => array(self::TYPE_TEXT, self::TYPE_HTML),
				'default_value' => self::TYPE_TEXT,
				'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_TEXT_TYPE_FIELD'),
			)),
			'DETAIL_PICTURE' => new Main\Entity\IntegerField('DETAIL_PICTURE', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_PICTURE_FIELD'),
			)),
			'DETAIL_TEXT' => new Main\Entity\TextField('DETAIL_TEXT', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_TEXT_FIELD'),
			)),
			'DETAIL_TEXT_TYPE' => new Main\Entity\EnumField('DETAIL_TEXT_TYPE', array(
				'values' => array(self::TYPE_TEXT, self::TYPE_HTML),
				'default_value' => self::TYPE_TEXT,
				'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_TEXT_TYPE_FIELD'),
			)),
			'SEARCHABLE_CONTENT' => new Main\Entity\TextField('SEARCHABLE_CONTENT', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_SEARCHABLE_CONTENT_FIELD'),
			)),
			'WF_STATUS_ID' => new Main\Entity\IntegerField('WF_STATUS_ID', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_STATUS_ID_FIELD'),
			)),
			'WF_PARENT_ELEMENT_ID' => new Main\Entity\IntegerField('WF_PARENT_ELEMENT_ID', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_PARENT_ELEMENT_ID_FIELD'),
			)),
			'WF_NEW' => new Main\Entity\EnumField('WF_NEW', array(
				'values' => array('N', 'Y'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_NEW_FIELD'),
			)),
			'WF_LOCKED_BY' => new Main\Entity\IntegerField('WF_LOCKED_BY', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_LOCKED_BY_FIELD'),
			)),
			'WF_DATE_LOCK' => new Main\Entity\DatetimeField('WF_DATE_LOCK', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_DATE_LOCK_FIELD'),
			)),
			'WF_COMMENTS' => new Main\Entity\TextField('WF_COMMENTS', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_WF_COMMENTS_FIELD'),
			)),
			'IN_SECTIONS' => new Main\Entity\BooleanField('IN_SECTIONS', array(
				'values' => array('N', 'Y'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_IN_SECTIONS_FIELD'),
			)),
			'XML_ID' => new Main\Entity\StringField('XML_ID', array(
				'validation' => array(__CLASS__, 'validateXmlId'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_XML_ID_FIELD'),
			)),
			'CODE' => new Main\Entity\StringField('CODE', array(
				'validation' => array(__CLASS__, 'validateCode'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_CODE_FIELD'),
			)),
			'TAGS' => new Main\Entity\StringField('TAGS', array(
				'validation' => array(__CLASS__, 'validateTags'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_TAGS_FIELD'),
			)),
			'TMP_ID' => new Main\Entity\StringField('TMP_ID', array(
				'validation' => array(__CLASS__, 'validateTmpId'),
				'title' => Loc::getMessage('ELEMENT_ENTITY_TMP_ID_FIELD'),
			)),
			'SHOW_COUNTER' => new Main\Entity\IntegerField('SHOW_COUNTER', array(
				'default_value' => 0,
				'title' => Loc::getMessage('ELEMENT_ENTITY_SHOW_COUNTER_FIELD'),
			)),
			'SHOW_COUNTER_START' => new Main\Entity\DatetimeField('SHOW_COUNTER_START', array(
				'title' => Loc::getMessage('ELEMENT_ENTITY_SHOW_COUNTER_START_FIELD'),
			)),
			'PREVIEW_PICTURE_FILE' => new Main\Entity\ReferenceField(
				'PREVIEW_PICTURE_FILE',
				'Bitrix\File\File',
				array('=this.PREVIEW_PICTURE' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'DETAIL_PICTURE_FILE' => new Main\Entity\ReferenceField(
				'DETAIL_PICTURE_FILE',
				'Bitrix\File\File',
				array('=this.DETAIL_PICTURE' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'IBLOCK' => new Main\Entity\ReferenceField(
				'IBLOCK',
				'Bitrix\Iblock\Iblock',
				array('=this.IBLOCK_ID' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'WF_PARENT_ELEMENT' => new Main\Entity\ReferenceField(
				'WF_PARENT_ELEMENT',
				'Bitrix\Iblock\Element',
				array('=this.WF_PARENT_ELEMENT_ID' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'IBLOCK_SECTION' => new Main\Entity\ReferenceField(
				'IBLOCK_SECTION',
				'Bitrix\Iblock\Section',
				array('=this.IBLOCK_SECTION_ID' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'MODIFIED_BY_USER' => new Main\Entity\ReferenceField(
				'MODIFIED_BY_USER',
				'Bitrix\User\User',
				array('=this.MODIFIED_BY' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'CREATED_BY_USER' => new Main\Entity\ReferenceField(
				'CREATED_BY_USER',
				'Bitrix\User\User',
				array('=this.CREATED_BY' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
			'WF_LOCKED_BY_USER' => new Main\Entity\ReferenceField(
				'WF_LOCKED_BY_USER',
				'Bitrix\User\User',
				array('=this.WF_LOCKED_BY' => 'ref.ID'),
				array('join_type' => 'LEFT')
			),
		);
	}
	/**
	 * Returns validators for NAME field.
	 *
	 * @return array
	 */
	public static function validateName()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}

	/**
	 * Returns validators for XML_ID field.
	 *
	 * @return array
	 */
	public static function validateXmlId()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}
	/**
	 * Returns validators for CODE field.
	 *
	 * @return array
	 */
	public static function validateCode()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}
	/**
	 * Returns validators for TAGS field.
	 *
	 * @return array
	 */
	public static function validateTags()
	{
		return array(
			new Main\Entity\Validator\Length(null, 255),
		);
	}
	/**
	 * Returns validators for TMP_ID field.
	 *
	 * @return array
	 */
	public static function validateTmpId()
	{
		return array(
			new Main\Entity\Validator\Length(null, 40),
		);
	}

	/**
	 * Add iblock element.
	 *
	 * @param array $data			Element data.
	 * @return Main\Entity\AddResult
	 */
	public static function add(array $data)
	{
		$result = new Main\Entity\AddResult();
		$result->addError(new Main\Entity\EntityError(
			Loc::getMessage('ELEMENT_ENTITY_MESS_ADD_BLOCKED')
		));
		return $result;
	}

	/**
	 * Updates iblock element by primary key.
	 *
	 * @param mixed $primary		Element primary key.
	 * @param array $data			Element data.
	 * @return Main\Entity\UpdateResult
	 */
	public static function update($primary, array $data)
	{
		$result = new Main\Entity\UpdateResult();
		$result->addError(new Main\Entity\EntityError(
			Loc::getMessage('ELEMENT_ENTITY_MESS_UPDATE_BLOCKED')
		));
		return $result;
	}

	/**
	 * Deletes iblock element by primary key.
	 *
	 * @param mixed $primary		Element primary key.
	 * @return Main\Entity\DeleteResult
	 */
	public static function delete($primary)
	{
		$result = new Main\Entity\DeleteResult();
		$result->addError(new Main\Entity\EntityError(
			Loc::getMessage('ELEMENT_ENTITY_MESS_DELETE_BLOCKED')
		));
		return $result;
	}
}


И пример работы с этой сущностью
//Выборка данных
$dbElements = Bitrix\Iblock\ElementTable::query()
	->setFilter(['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'])
	->setSelect(['NAME', 'ID', 'DETAIL_PAGE_URL', 'DATE_ACTIVE_FROM'])
	->addSelect('IBLOCK_SECTION_ID', 'PARENT_SECTION')
	->setLimit(10)
	->addOrder('id', 'DESC')
	->exec();

while ($arElement = $dbElements->fetch()) {
	echo "{$arElement['NAME']} - " . $arElement['DATE_ACTIVE_FROM']->format('d.m.Y H:i:s');
}

//Добавление записи
$addResult = Bitrix\Iblock\ElementTable::add([
	'NAME' => 'Название нового элемента', 
	'IBLOCK_ID' => CATALOG_IBLOCK_ID
]);
if (!$addResult->isSuccess()) {
	echo implode('<br>' ,$addResult->getErrorMessages());
}




Битрикс очень гордится своим модулем Highload-блоков, который полностью написан с использованием D7.
Раньше у них в качестве хранилища произвольного набора информации были только инфоблоки. Инфоблок, для тех кто не в курсе, это такая сущность, которая в БД хранится как комплекс из нескольких таблиц (1 таблица на «базовые» поля элемента инфоблока и до 2х таблиц на свойства элемента инфоблока). Все базовые поля элементов всех инфоблоков хранятся в одной таблице. Если у вас будет 15 инфоблоков, в каждом из которых будет по 500к элементов, все эти элементы по факту будут находиться в одной таблице. Дополнительные свойства элементов инфоблоков джойнятся из других таблиц. Если это инфоблоки первой версии, то все свойства всех инфоблоков также лежат в одной таблице, а в случае с инфоблоками 2.0 (привет, маркетинг) — свойства каждого инфоблока уже разделены по разным таблицам.
И все это дело естественно очень сильно тормозило уже на относительно небольших наборах данных. 400к элементов в одном инфоблоке уже довольно сильно затормаживают работу админки. Маркетологи в битриксе подумали, и запилили Highload-блоки! Разница в реализации между обычными инфоблоками — минимальная. Теперь для каждого highload-блока создается своя таблица + дополнительно создается еще одна таблица для хранения множественных значений. Обычный подход в создании обычной таблицы в БД они назвали гордым именем highload просто потому, что оно тормозит меньше обычных инфоблоков!
Кроме того, внутри модуля, для того, чтобы он работал согласно D7, классы сущностей генерируются динамически и eval'ятся на каждом хите. Вот такой вот highload.
Посмотреть на это
public static function compileEntity($hlblock)
    {
        global $USER_FIELD_MANAGER;

        // generate entity & data manager
        $fieldsMap = array();

        // add ID
        $fieldsMap['ID'] = array(
            'data_type' => 'integer',
            'primary' => true,
            'autocomplete' => true
        );

        // build datamanager class
        $entity_name = $hlblock['NAME'];
        $entity_data_class = $hlblock['NAME'];

        if (!preg_match('/^[a-z0-9_]+$/i', $entity_data_class))
        {
            throw new Main\SystemException(sprintf(
                'Invalid entity name `%s`.', $entity_data_class
            ));
        }

        $entity_data_class .= 'Table';

        if (class_exists($entity_data_class))
        {
            // rebuild if it's already exists
            Entity\Base::destroy($entity_data_class);
        }
        else
        {
            $entity_table_name = $hlblock['TABLE_NAME'];

            // make with an empty map
            $eval = '
                class '.$entity_data_class.' extends '.__NAMESPACE__.'\DataManager
                {
                    public static function getTableName()
                    {
                        return '.var_export($entity_table_name, true).';
                    }

                    public static function getMap()
                    {
                        return '.var_export($fieldsMap, true).';
                    }

                    public static function getHighloadBlock()
                    {
                        return '.var_export($hlblock, true).';
                    }
                }
            ';

            eval($eval);
        }

        // then configure and attach fields
        /** @var \Bitrix\Main\Entity\DataManager $entity_data_class */
        $entity = $entity_data_class::getEntity();

        $uFields = $USER_FIELD_MANAGER->getUserFields('HLBLOCK_'.$hlblock['ID']);

        foreach ($uFields as $uField)
        {
            if ($uField['MULTIPLE'] == 'N')
            {
                // just add single field
                $field = $USER_FIELD_MANAGER->getEntityField($uField, $uField['FIELD_NAME']);
                $entity->addField($field);

                foreach ($USER_FIELD_MANAGER->getEntityReferences($uField, $field) as $reference)
                {
                    $entity->addField($reference);
                }
            }
            else
            {
                // build utm entity
                static::compileUtmEntity($entity, $uField);
            }
        }

        return Entity\Base::getInstance($entity_name);
    }


Черт бы с ним, но эти самые хайлоад блоки ну никак не могут выступать в роли альтернативы обычным инфоблокам. Оказывается их придумали только для того, чтобы хранить справочные не-иерархичные данные. Кроме того, модуль до сих пор не поддерживает таких нужных функций в админке, как фильтрация по полю типа «Дата», нельзя обозвать сущность HLблока каким-то человекопонятным названием, чтобы администратор каждый раз не пугался при входе на страницу редактирования сущности, к примеру, BrandReference. Все это наводит на мысль, что задумывалось это дело ради альтернативы медленным инфоблокам, но допилить не успели (или не осилили, или пошло в разрез с интересами бизнеса), и в итоге зарелизили полу-готовый функционал как новую фишку, а маркетологи причесали и красиво подали эту идею.

C — Controller, или компонент


Обычный компонент в битриксе можно сравнить с виджетами из Yii. Это некий контейнер, обособленный от всех остальных контейнеров, который принимает на вход какие-то параметры, делает какую-то работу, и с результатом работы подключает вьюху. Разработчики битрикс глубоко убеждены, что те компоненты, которые они предоставляют из коробки, решают большинство задач, которые стоят перед их коллегами. Но, как водится, разработчикам же ничего не нравится всегда, и возможностей стандартных компонентов им всегда «немного» не хватает. Поэтому Битриксоиды решили дать разработчикам возможность модифицировать результат работы компонента… с помощью вьюхи. В директории шаблона компонента можно создать файлик result_modifier.php, в котором можно дополнить результат работы компонента своими данными. И если вы вдруг захотите использовать эти данные в другом шаблоне, вам придется скопипастить этот файлик (ну или заинклюдить этот файл из другого шаблона). Меня всегда мучал вопрос — для чего этот пафос? Почему бы не добавить горку запросов прям в php шаблоне? Разница то невеликая получается.
Что это я про шаблоны в разделе о контроллерах…

В битрикс есть 2 вида компонентов 2.0 (опять привет маркетинг) — обычные и комплексные. Обычный компонент — это виджет. Комплексный компонент — это некий контроллер+роутер, который на основании URL понимает, какую именно страницу с набором виджетов нужно отобразить. Порядок работы примерно такой:
  • в url написано /catalog/bolshaya-zelenaya-shapka.html
  • с помощью mod_rewrite битрикс понимает, что для физического раздела /catalog нужно всегда подключать файл /catalog/index.php
  • комплексный компонент парсит url, и понимает, что нужно подключить детальную страницу товара, назовем ее detail
  • комплексный компонент собирает параметры, которые необходимы для работы его дочерних компонентов
  • комплексный компонент подключает свой шаблон detail.php, внутри которого прописано подключение дочерних обычных компонентов


С виду не очень красиво, но работать можно. Однако не все так просто… Если вы с помощью визуального редактора поменяете параметры комплексного компонента, то файлик с настройками адресации (urlrewrite.php) будет перезаписан системой. Причем, если вы вдруг что-то неправильно там написали для других страниц обязательно что-то сломается без какого-либо предупреждения. На практике это может привести к потере работоспособности целых разделов сайта.
Настройка параметров комплексного компонента может превратиться в муку. У одного такого компонента с легкостью может быть сотня входных параметров, просто потому, что нужно настраивать параметры дочерних компонентов.
Комплексный компонент — он вроде бы и роутер. Однако все те маршруты, которые вы создадите в этом компоненте, не попадут в автоматически генерируемый sitemap.xml. Эти ссылки не попадут в модуль поиска. У вас не будет никакой возможности сгенерировать адрес до маршрута извне (например, вы хотите поставить ссылку на детальную страницу бренда где-то в сайдбаре, и нельзя будет обратиться к роутеру с просьбой сгенерить этот URL).

Вообще говоря, функции роутера в битриксе толком не выполняет никто. В инфоблоках есть возможность настроить шаблон URL для страницы инфоблока, страницы раздела инфоблока и страницы элемента инфоблока. Все, у инфоблоков больше не может быть страниц.
Для форумов есть возможность настроить шаблоны некоторых страниц. Для блогов можно настроить. Возможно, где-то еще можно что-то настроить… все это настолько децентрализовано, что собрать воедино это все становится достаточно тяжело.

Обычные компоненты — это чуть более простые сущности, чем комплексные компоненты. Их задача — принять на вход набор параметров, обработать их, скормить результат работы шаблону и закешировать все и вся.
Вся логика компонента содержится в файле component.php. С 12й версии битрикса (ныне актуальной является версия 16, 4 года прошло) появилась возможность «использовать ООП» в компонентах. Это нововведение заключается в том, что вместо файла component.php можно создать файл class.php, в котором можно вместо обычной лапши написать класс, унаследованный от \CBitrixComponent. И это был большой шаг вперед, т.к. появилась возможность наследовать компоненты и не использовать result_modifier.php вообще, и не практиковать копипасту, если нужно вдруг сильно кастомизировать компонент. Но и тут до сих пор все не так ладно. Из всего набора компонентов, лишь процентов 25-30 могут похвастать наличием класса в своем арсенале. Причем добрая половина из них просто не даст вам возможности расширить себя полностью, т.к. написаны они часто нелогично.
К слову, добрые люди пытаются стандартизировать, как-то помочь разработчикам в написании компонентов, и есть соответствующий инструментарий

V — View, или шаблоны


Шаблоны в битриксе можно разделить на несколько типов:
  • Шаблоны обычных и комплексных компонентов 2.0
  • Шаблоны сайта
  • Шаблоны прочих сущностей (почтовых отправлений, выпусков рассылок, веб-форм, генераторов экспортов и еще много чего)


В шаблонах компонентов есть даже возможность использовать шаблонизаторы. В принципе можно подключить любой шаблонизатор, но из коробки никаких вспомогательных инструментов нет. Если кому надо, есть у меня пара ссылок на расширения для twig и blade, которые работают и вполне себе используются на продакшене. Но и тут битриксоиды извратились. Шаблонизатор можно использовать только с компонентами. Подключить шаблонизатор к рендереру шаблона сайта, или других сущностей не получится, т.к. нет там никакого рендерера.

В шаблонах компонентов раздражает еще момент с их размещением. Компонент подключается с помощью нехитрой контструкции
$APPLICATION->IncludeComponent('bitrix:catalog.section', 'template_name', []);

В качестве второго параметра идет название шаблона компонента. Так вот в зависимости от различных условий, местоположение этого шаблона может быть в самых неожиданных местах:
  • bitrix/components/bitrix/catalog.section/templates/template_name
  • local/components/bitrix/catalog.section/templates/template_name
  • bitrix/templates/.default/components/bitrix/catalog.section/template_name
  • bitrix/templates/site_template/components/bitrix/catalog.section/template_name
  • local/templates/.default/components/bitrix/catalog.section/template_name
  • local/templates/site_template/components/bitrix/catalog.section/template_name
  • bitrix/components/bitrix/catalog/templates/.default/bitrix/catalog.section/template_name
  • local/templates/site_template/components/bitrix/catalog/.default/bitrix/catalog.section/template_name

И это еще я не все варианты перечислил…

Шаблон сайта можно рассматривать как набор файлов: header.php, footer.php (да, у сайта обязательно они должны быть), description.php (системное описание шаблона сайта), template_styles.css (стили шаблона сайта), директорию с шаблонами компонентов и еще группка менее значимых файликов. И все. И никак на это не повлиять, ничего с этим не сделать. Невозможно подцепить шаблонизатор.

Про другие шаблоны и говорить нечего. Они либо просто хранятся в БД в виде верстки со включением в нее каких-то «переменных» данных, либо это тупой php файл, который делает всю работу, от выборки параметров из БД до вывода информации. Для примера, можно посмотреть на генератор YML файла для маркета. Нет никакого смысла выкладывать его сюда, просто потому, что он достаточно большой, около 2к строк. Кому нужно, тот нагуглит, лежит он в /bitrix/modules/catalog/load/yandex_run.php

Файловая природа



Как стало ясно выше, в битриксе с архитектурой все не очень хорошо. Но есть у битрикса и еще один важный аспект архитектуры.
Битрикс — это на половину файловая CMS. Очень многие вещи управляются с помощью каких-то файлов:
  • Нужна страница — создай файл
  • Нужен набор страниц — создай файл и подключи там компонент, работающий с инфоблоками
  • Нужно задать title для страницы — отредактируй файл
  • Нужно задать title для всех страниц раздела — создай специальный файл .section.php в корне этого раздела
  • Нужно отредактировать права — редактируй файл .access.php
  • Настройки до инициализации системы — в файле dbconn.php, .settings.php и .settings_extra.php
  • result_modifier.php, component_epilog.php, init.php, .parameters.php, .description.php ....


И таких специальных файликов по битриксу разбросано огромное множество. С одной стороны, это дает определенную гибкость при работе с системой. С другой — это может превратиться в муку как для разработчика, так и для менеджера сайта. Файлы страниц иногда превращаются в кашу из php кода, верстки, и подключаемых компонентов. В результате визуальный редактор может некорректно распарсить этот файл, и при редактировании он запросто может экранировать php теги в некоторых местах, что приведет к неработоспособности страницы. Вы скажете — не надо писать php код в таких файлах? Да, я знаю. Но битрикс очень часто и безальтернативно заставляет так поступить.
Да и в голове нужно держать постоянно информацию о том, что это за файлы такие, и какие данные они могут содержать. В разных файлах должны содержаться разные данные с разной структурой, и нужно ее помнить для каждого варианта. В документации искать это каждый раз — тяжелый труд.

В дополнение к вышесказанному



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


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

Админка


Если кто-то работал с админкой, создавал свои страницы в административной части так, как это предлагает делать битрикс, тот меня поймет. Это просто ад. Для тех, кто не в курсе — битрикс предлагает для каждой страницы использовать файл с лапшой. Например, страница детального просмотра заказа в админке в исполнении разработчиков битрикса занимает over 4к строк. У меня IDE начинает подтормаживать при просмотре содержимого этого файла. Там тебе и php, и js, и html. Хорошо хоть, от SQL избавились, хотя я уверен, что на других административных страницах он есть.
И что мешало сделать работу административных страниц с помощью тех же компонентов — не понятно. Кастомизировать большинство административных страниц просто нет никакой возможности. В случае с компонентами это можно было бы сделать в два счета.
К слову, добрые люди сделали модуль, который поможет вам в построении административных страниц

js-фреймворк


В битриксе есть js составляющая, которая выполняет роль некоего клиентского фреймворка. Никто из разработчиков не любит его по нескольким причинам:
  • он почти не документирован
  • он монструозен
  • он во многом дублирует привычный многим jquery


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

Дух копипасты


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

Asset-management и CDN


Очень «мне нравится» в битриксе способ управления ресурсами. В принципе, можно зарегистрировать набор определенных «библиотек». Каждая библиотека — это набор css/js файлов, который может зависеть от каких-то других библиотек. Если подключить какую-то библиотеку на страницу, то перед ее подключением будут разрешены все зависимости и все зависимые библиотеки будут вставлены на страницу. Все вроде бы хорошо, только каждый ресурс будет вставлен в виде отдельного файла в тег script или link. И благодаря этому существуют сайты, у которых подключено по 30-50 скриптов и столько же файлов стилей.
Говно-вопрос, сказали в битриксе, и сделали волшебную галочку, которая объединяет все эти файлы в один. И появились сайты, где вместо 50 скриптов стало 2, каждый по 300-500кб. Какое-то время назад это объединение работало с ошибками и объединяло одни и те же ресурсы по нескольку раз, но сейчас вроде бы исправили.
И тут битриксоиды выкрутились — прикрутили возможность выгрузить все ресурсы на CDN сервер. Который вечно отваливается…
Потом появился Google Pagespeed Insights, который рекомендовал опустить все ресурсы в нижнюю часть страницы. И в битриксе опять сделали волшебную галочку, которая тупо опускает все ресурсы в body, если они не помечены специальным атрибутом.
А еще они вместе с коробкой распространяют минифицированные версии своих скриптов, которые подключаются при использовании еще одной волшебной галочки в админке.
В общем, никаких вам scss, никаких TypeScript. Хотите грамотно управлять ресурсами — не используйте встроенную систему битрикса, юзайте webpack, который можно спокойно с битриксом подружить.

Многосайтовость / многоязычность


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

Механизм управления правами


Очень замудрили с этой подсистемой. Часто бывает сложно разобраться, почему ты выдал права на просмотр какой-то сущности, а пользователь не может ими воспользоваться. Например, чтобы дать право на редактирование инфоблока, нужно дать доступ к директории /bitrix/admin, выдать права для конкретного инфоблока и выдать права в главном модуле. Чересчур много операций нужно сделать, чтобы выдать права для одной сущности. А если прав не хватает, то без ковыряния в исходниках никак не получится понять — почему.

Конфигурирование


В битриксе нет централизованного хаба, который бы позволил управлять настройками системы. Настройки опять таки децентрализованы по всей системе. Опции есть в настройках модуля, в настройках компонентов, в COption (будучи не вынесенными в админку). В админке опции одного модуля могут быть разнесены по 3-4м разным страницам, которые находятся в совершенно разных местах. urlrewrite можно править через админку! Теперь еще и .settings и .settings_extra. Иногда совершенно не ясно, какие из них более приоритетны, очень часто не хватает пояснений для опций, непонятны взаимосвязи. Нет никакого нативного способа расшаривать конфигурацию между разработчиками.
Настройки бывают очень нелогичными. Иногда доходит до абсурда… посмотрите компонент бигдаты — разве его сможет настроить неподготовленный человек?

Интеграция с 1С


Это тот пункт в списке фич битрикса, на который клюет достаточно большое количество заказчиков. Битрикс обещает в 2 клика настроить двустороннюю интеграцию сайта с 1С, которая будет мгновенно доставлять контент и документы от одной системы к другой.
Да, оно действительно так и есть, но с несколькими оговорками.
Во-первых, чтобы сделать интеграцию «из коробки» без дополнительных усилий, нужно сделать все именно так, как написано в документации битрикса — построить каталог на сайте по тем правилам, которые предлагает битрикс и построить каталог в 1С, которые требует битрикс. В идеале — создать вообще все с нуля, и тогда может быть, у вас все заработает из коробки.
Во-вторых, Битрикс дружит не со всеми конфигурациями 1С из коробки. Стоит предварительно ознакомиться
В-третьих, идеального мира не бывает. Обычно у заказчика, который хочет сайт, уже есть розничный бизнес, а значит уже есть 1С, которая является огромной мусоркой. И эту мусорку приходится прокидывать на сайт. А чтобы на сайте не получилось такой же мусорки, требуется значительно доработать механизм обмена.
Очень часто требования заказчика сильно расходятся с тем видением продукта, которое сформировано у команды Битрикса, и тогда доработка механизма обмена может быть достаточно дорогой, по трудоемкости сопоставимой с разработкой уникального модуля обмена под конкретный случай.
Поэтому не нужно пытать иллюзий по поводу того, что вам удастся легко интегрировать сайт с 1С. Это все происки маркетологов.

Доработка обмена с 1С — это тоже отдельная тема. За организацию обмена каталогом отвечает класс \CIBlockCMLImport.- 5.7к строк. Один из главных методов, который чаще всего требует расширения — \CIBlockCMLImport::ImportElement, содержит больше 1к строк. Достаточно раз унаследоваться, пару раз обновить продукт на протяжении длительного времени, и можно получить неработающий обмен с 1С. Поэтому часто разработчики не лезут в этот класс и пытаются как-то влезть в процесс импорта с помощью обработчиков событий. Работать с обработчиками событий в битриксе, особенно в модуле инфоблоков — тоже не очень приятное занятие, хотя бы из-за того, что однотипные события устроены не однородно, а некоторых событий просто не хватает.
В общем с этим дела обстоят также печально, как и ранее.

Несогласованность


Мне порой кажется, что разработчики разных модулей не особенно то общаются между собой. Изучая исходники ядра натыкаешься на очень разнородные решения, которые можно было бы выполнить на одном движке, но они реализованы почему-то по разному.
Для примера можно взять свойства элементов инфоблоков и UserFields. И та и другая сущность по факту является дополнительным полем для другой сущности. Она имеет тип, имеет значение и описание. Значение хранится в отдельной(ых) таблице(ах) БД, имеют примерно схожий интерфейс доступа к данным. Так почему бы не сделать для них одинаковый интерфейс?
Вот в конце марта обновился модуль sale до последней версии, и там тоже обещали произвольные свойства для заказов. Неужели там теперь новый, третий интерфейс для работы со расширенными свойствами сущности?

Битрикс24


Это вообще отдельная тема для разговора. На почве этой системы часто возникает путаница. Есть 2 варианта исполнения Б24 — SaaS и Standlone. Есть маркетплейс для Б24, но в нем содержатся приложения только для SaaS версии! Если у вас коробочная версия, купленная за 200 кусков, вы не сможете поставить такое популярнейшие приложения, как конструктор документов, да и вообще вы не сможете на свой Битрикс24 поставить ни одно приложение из маркетплейса для Битрикс24. Вот такой парадокс.
Вместо этого в вашем Битрикс24 будет доступен маркетплейс от обычной версии. Там решений гораздо больше, но они сконцентрированы в основном вокруг Управления Сайтом, а не Б24.

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

К слову, дорабатывать компоненты в коробочной версии Б24 — та еще задачка. Компоненты, которые генерируют js код, который с помощью ajax обращается к php коду, который в ответ генерирует html+js. Это адовая смесь, в которую очень не хочется погружаться.

Документация


Документация по битриксу отстает от развития продукта на 1-1.5 года. Код очень слабо покрыт phpDoc'ами, и часто комментарий перед классом стоит исключительно «для галочки», будучи автоматически сгенерированным в IDE.
Сам стиль изложения документации в официальных источниках часто бывает слишком «вольным», а содержимое некоторых статей в документации может не иметь никакого отношения к самому битриксу.
Курс разработчика имеет очень много информации, однако формат, в котором разработчика знакомят с возможностями системы, не дает того уровня восприятия, который требуется. Если вы зайдете в Cookbook Symfony, то там все разложено по полочкам, расписаны все необходимые аспекты в зависимости от версии. Тогда как в битриксе курс обучения разработчика содержит непонятно по какому принципу структурированную информацию по старому и новому ядру, которая подается сначала отдельно, а потом вперемешку, от чего у начинающих возникает головная боль.

Организация процесса разработки


Из-за специфичности системы не так уж просто организовать удобный процесс разработки. Не самая свежая версия редакции Бизнес (что была под рукой) после установки занимает, вдумайтесь, почти 530 мегабайт
$ du -s *|sort -nr|cut -f 2-|while read a;do du -hs $a;done
523M	bitrix
204K	upload
 64K	bitrixsetup.php
 56K	desktop_app
 20K	readme.html
 20K	license.html
4,0K	web.config
4,0K	urlrewrite.php
4,0K	readme.php
4,0K	license.php
4,0K	install.config
4,0K	index.php

Из этого объема добрая половина — это бинарники и установочники, которые в общем-то не нужны для версионного контроля. Вообще говоря, принято не версионировать битриксовое ядро. Разработчики Битрикса как бы сами гарантируют целостность ядра, управляют сами зависимостями версий разных модулей при обновлениях. Но это несет в себе сразу, как минимум, один большой минус — невозможно одной командой из версионного контроля развернуть полностью работающий проект, приходится собирать его по частям: исходники ядра доставать из битриксового бекапа, а исходники разработчиков — из git.
С базой тоже все не ладно. Если сами вы можете использовать миграции при разработке, то битрикс накатывает обновления в базу с помощью обычных скриптов, которые вы не можете контролировать. Поэтому при обновлениях все равно придется перекидывать бекапы баз от центрального хоста разработки к другим разработчикам.
Добрые люди, опять же, пилят инструменты, которые помогают это все организовать, но заставить битрикс следовать этим правилам к сожалению до сих пор не удается.
Официально битрикс разрешает иметь 2 копии одного дистрибутива. Один — для продакшена, второй для разработки. Если у вас несколько разработчиков на одном проекте — то вы, как бы, вне закона ) На самом деле, достаточно отрубить машине с битриксом входящие и исходящие подключения с/на www.bitrixsoft.com, и тогда можно наклепать сколь угодно много копий разработки, просто они не смогут самостоятельно обновляться.

Коллеги


И последний вопрос, которого хотелось бы коснуться.
В связи с тем, что битрикс имеет низкий порог вхождения, среди компаний, которые предоставляют услуги на этом рынке очень много неквалифицированных кадров. Мне довелось повидать множество различных проектов за свою карьеру (суммарно более сотни), выполненных на 1С-Битрикс. Могу с уверенностью сказать, что 95% из них были выполнены «тяп-ляп». Очень редко попадались проекты, к разработке которых чувствовался подход, однако это были единицы. Это все очень печально.

Выводы


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

Какие тут можно сделать выводы. Битрикс — крайне сложная система в связи с тем, что имеет непродуманную архитектуру, множество изъянов, которые так и продолжают жить в продукте на протяжении длительного времени. С другой стороны Битрикс — это достаточно простая система, которая для старта требует гораздо меньший уровень квалификации, в отличие от фреймворков.
Поддержка этого продукта — весьма неблагодарное занятие, по сравнению с такими продуктами, как Symfony, Laravel, Yii. Продукт очень любит вставлять палки в колеса как неопытным, так и опытным разработчикам, что, в свою очередь, может отражаться и на стоимости услуг опытных разработчиков под Битрикс.

Жалею ли я о том, что так много времени потратил на работу с этой системой? Скорее да, чем нет. Разумнее было бы потратить это время на изучение чего-то более правильного и более логичного (чем я стараюсь активно заниматься сейчас). Но так уж получилось, что некому было меня направить в правильное русло в начале моего пути.

Если вы — начинающий php разработчик, то предпочтите Битриксу изучение фреймворков, таких как Symfony, Laravel, Yii, ZendFramework. Поверьте, в будущем это с лихвой окупится. Освоив любой из этих фреймворков вам не составит труда в будущем разрабатывать что-то под Битрикс. Если у вас нет выбора, то изучайте Битрикс, но в свободное время лучше все-таки пытаться погрузиться в мир фреймворков, чтобы поставить мозги на место.

Если вы — разработчик со стажем в Битрикс, но без опыта в других фреймворках, то обязательно окунитесь в другой мир, вам откроется очень много новых и полезных знаний, которые помогут вам в написании гораздо более качественных решений под 1С-Битрикс. Старайтесь использовать решения из других фреймворков в своих проектах, благо сделать это совсем несложно благодаря компонентному подходу последних и composer.

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

UPD. Видно, что статью прочитали сотрудники битрикса. В разделе про Маркетинг я писал, что в разделе Архитектура в курсе разработчика Битрикс написаны маркетинговые призывы. Теперь их там нет. Даже опечатались, видимо, торопились очень.
image

Спасибо nook за наблюдательность и зоркий глаз :)
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 177
  • +25
    мыши плакали, кололись, но продолжали есть кактус
  • +2
    Первая часть «Вместо вступления» очень хорошо описана и я согласен с вами, но на самом деле так можно сравнивать любую CMS с любым фреймворком.
    Статья в целом все верно описывает, хоть и замечаются нотки страдания автора. Но как верно было замечено, маркетологи в битриксе знают свою работу и это позволяет быть продукту востребованным на рынке.
    • +9
      Тут есть определенная доля несправедливости. Ведь ничего же не мешает авторам Битрикса следовать тем же стандартам, что и фреймворки. Однако они упорно игнорируются. И это одна из самых больших проблем. Авторы CMS просто не прислушиваются к сообществу, и игнорируют его потребности.
      Возьмите тот же Drupal. Они прислушались к сообществу и положили в основу 8й версии Symfony. Да, там тоже есть свои тонкости и шероховатости, но в целом оно стало на много лучше.
      Если разработчики Битрикса прислушаются к таким постам, то возможно в будущем мы сможем получить годный коммерческий продукт.
      • +1
        Согласен с вами, но согласитесь тогда им придется переработать огромное количества кода, а это думаю будет им в убыток. С точки зрения бизнеса, потратить (все условно) 6 месяцев на разработку фичи/модуля или же это время на переработку старого, им выгоднее первое потому что во время презентации лучше сказать «Вы внедрили новую технологию „ФАу-хрень“» чем «Знаете мы тут пол года переработали старый код, так что нечего нового нету, но зато теперь это элегантный код, и ваши программисты будут визжать от счастья»
        • +6
          Дык это элементарно продаётся же.
          Вот пример: «Мы переработали ядро на ядро Yii. Теперь найти разработчиков станет проще, с скорость генерации страниц увеличилась в 10 раз, теперь гугл сильнее полюбит ваши сайты и вы будете в топе поисковых систем!».
          Или так:
          «Мы переработали ядро, теперь это Битрикс 2.0 — еще лучше чем раньше. Вы можете сделать свой новый проект на битрикс управление сайтом старой версии, или же использовать Битрикс 2.0 и получить огромное конкурентное преимущество.»
          • 0
            1. Начальник: — Не, это наш конкурент, он забыкует и поменяет-что-нибудь и мы деньги потеряем.

            2. С точки разработчика станет непонятно, зачем изучать Битрикс 2.0, если есть, например, Yii?
          • +2
            Да ведь по тексту куча ссылок на решения, которые уже сильно улучшают ситуацию.
            Могли бы интегрировать это, использовать в своих решениях, пойти на диалог с сообществом.

            Но работа с коммьюнити ограничивается проведением конкурсов на лучшего чат-бота.
          • +1
            Спасибо за нормально читаемый и вполне аргументируемый пост!
            • 0
              Прочитал Вашу статью по поводу архитектуры и замыкания «в самом себе» согласен с каждым словом, и вообще многое читал как будто про меня написано…

              Но есть один момент, я 10 лет разрабатываю системы на платформе 1С. Не смотря на то что платформа 1С это абсолютно другой проект, проблемы на 90% такие же.
            • 0
              Проблема в том, что тогда придется отказаться от обратной совместимости. И все те, кто уже, например, лет 5 поддерживают и развивают свой сайт встанут перед выбором того, что они больше не получат обновлений и нового функционала, а если они хотят новинки, то им нужно сделать свой сайт с нуля на новом битриксе.
              • 0
                Когда началась вся эта заваруха с D7, на конференции Битрикс всем так и сказал, что новое ядро не будет совместимо со старым вообще никак, мол «надо двигаться вперед, отказываться от устаревших решений» и т.д. Видюшку даже могу найти, где Рыжиков об этом рассказывает.
                Однако нет, в итоге они решили пойти по пути обратной совместимости и слить старое и новое ядро, делая постепенный переход. Только вот совместимость от этого только страдает, регулярно что-то отваливается при обновлениях.А новый модуль интернет-магазина таки не имеет полной обратной совместимости совершенно официально.
                • 0
                  Ну так как заявлялось, так и делает. Ядра между собой не совместимы.
                  Но есть большое НО! Битрикс никогда не заявлял, что выйдет новая версия продукта, где не будет старого кода и только новый код и что откажутся вообще от принципа обратной совместимости. Т. е. весь старый код продолжает работать. Методы заменяются заглушками, которые под собой содержат вызовы нового ядра. Весь новый код пишется только на D7. А все это требует массу усилий.
                  Например инфоблоки: огромная головная боль, как это все переписать на D7 и ORM, но так чтобы и весь старый код работал.
                  Проще было бы с нуля реализовать все это по новому, убрав сразу все исторические недостатки, возможно даже частично реализовав функционал по новому.
                • 0
                  Для 1С это вообще не проблема: 1С 7.7 вообще не совместима с 8.0. Затем 8.1 совместимая с 8.0 затем 8.2 с Клиент-Серверной архитектурой, частично совместимая но по сути нужно все заново написать. Переход с 8.2 на 8.3 тоже задча не на на один месяц для больших проектов.
                  • 0
                    Все таки 1С: это другой случай. Цикл жизни 1С бухгалтерии иной, нежели сайта.
                    Да и если битрикс уже поддерживает обратную совместимость 10 лет, то нельзя вдруг все поменять. Да и в любом случае нужно будет поддерживать две версии (ведь старую нельзя совсем бросить)
            • +2
              Давеча делал анализ-разбор топа крупнейших интернет-магазинов в кое-какой сфере. Магазины на битриксе есть, да, в конце хвоста списка.
              Что интересно, каждый (еще раз, каждый) из них в течении 4-х дней, пока я с ними ковырялся или падал или тормозил. Тормозил — это я называю от 7 секунд на генерацию страницы. Падал — это обычная ошибка «DB query error. Please try later. ».
              Если говорить просто о топе интернет-магазинов в целом — то в нём (в самом конце) — только один битрикс — это эльдорадо. Прямо сейчас он генерирует свою страничку 1.7 секунд. И это не скорость загрузки картинок, не время загрузки страницы, это генерация страницы.

              Причем видно, что люди изгаляются, от стандартных компонентов отходят (господа хейтеры, прошу хотя бы в этом посте перестать мне доказывать, что я ничего не знаю, и разработчики магазинов используют стандартные изкоробочные модули).
              • 0
                А можно узнать что за сфера такая «кое-какая», и топ CMS которые используются, вангую Magento, Prestoshop или самописные.
                • 0
                  Если Эльдорадо, то, наверное, Бытовая техника и Электроника?
                  • 0
                    Не, эльдорадо это из ТОП 10 магазинов вообще: http://rusbase.com/news/top-100-ecommercer/ или http://www.ruward.ru/ecommerce-index-2015/
                  • +2
                    Кстати, какие CMS я не смотрел. А битриксы определялись сами по себе — если уродливые (субъективное) узнаваемые урлы в фильтрах, или же внезапные тормоза — лезу смотрю, да, битрикс.
                    Я посмотрю, если получится, отпишусь.
                    • 0
                      Кстати тоже это заметил, битрикс сразу в глаза бросается)
                      • +2
                        Первые топ 5 — не CMS. Остальные либо не определить, либо не знаком.

                        1. Фронтенд часть поверх CRM, не CMS
                        2. На Java или .NET (скорее Java) написано, не CMS
                        3. .NET
                        4. не CMS
                        5. не CMS

                        Разумеется, не Prestashop. Ребят, вы бы ещё youtube на джумле делали.
                        • 0
                          То есть стэк не PHP+Mysql, а упециализированные решения под конкретный бизнес? Тогда какой смысл их сравнивать с Битриксом который расчитан на широкую публику?
                          • +3
                            Потому что выше в комментариях Вы попросили. Я первоначально на это и не смотрел совсем.

                            Отвечая шире, можно сказать, что сравнение топовых магазинов с утилизированными решениями может охладить пыл некоторых заказчиков, которые должны решить, они «широкая публика» или хотят «как м.видео».
                          • +2
                            первый в http://rusbase.com/news/top-100-ecommercer/ Exist — это ASP.NET
                            • 0
                              Второй — скорее всего дотнет (они на ultima erp, которая написана на дотнете)
                              • 0
                                Не, ulima им только erp предоставляет, как сейчас, так и до того, как у них sap был. Кроме того, их (Ультимы) веб решение «просто добавь дизайн» использует битрикс (пруф) и Ultime EStore на Zend Framework (пруф).
                                А сайт делают не они, а ребята из DZ. Их профиль — Java, недавно (года 3-4) на .NET перешли.
                                • +1
                                  Однозначно Java, вот пруф первый https://www.ulmart.ru/jcaptcha?empty=1461670997852 вот пруф второй: http://dz.ru/portfolio/clients/ulmart/release_2-0/ (там Tomcat и Spring JDBC)
                              • +3
                                Используйте wappalyzer, так быстрее узнавать на чем сайт.
                          • +1
                            Эльдорадо не на битриксе. И генерирует главную за 60 мс.
                            • 0
                              Эльдорадо на битриксе, может тупо глянуть код страницы. только у них и версия не за 50к и команда разработчиков своя
                              • +1
                                Пруф: http://www.eldorado.ru/bitrix/js/main/core/css/core.css?1341055283

                                Ну и понятно, что я не про главную страницу. Главная всегда кешируется, ясное дело.
                                • +1
                                  Извиняюсь, у них главная похоже не на Битриксе: на ней нет заголовка X-Powered-CMS: Bitrix Site Manager, а вот на странице с товаром уже есть. Либо главная кешируется целиком чем-то сторонним.
                                  • 0
                                    У меня сайт с открытками http://www.ktokogda.com/ тоже не показывает никак заголовков о CMS и генерируется за 60 мс, скорость достигается с помощью композитного кеша, а отключить заголовки не сложно. На локалке при разработке показывает вообще 6 мс., видимо сеть съедает остальное.
                                    Кстати не вижу у них 60 мс, вижу 120 мс.
                                    Композитный кеш наверное единственное, за что можно поставить плюс Битриксу. С другой стороны порог вхождения очень низкий, и можно наломать больше дров если делать не зная толком технологию.
                                    • 0
                                      Если за тормозитный композитный кеш ставить плюс битриксу, то люди способные скопипастить microcache в конфиг nginx очень огорчатся
                                      • 0
                                        микрокеш никак не поможет вам с персональными данными, корзина, данные аккаунта и т.п., эти данные должны вычислиться и подгрузиться фоном после того как страница загружена из мемкеша, в битриксе это как раз и решили композитным кешом. Почему он тормозной? у меня страница генерируется за 6 мс на локальном компе.
                                        • 0
                                          Окей, почему мне не нравится:
                                          1. Не хочу платить 300к за снятие кнопочки
                                          2. Революции не вижу, аякс подгрузка блоков или ESI — вполне известная гражданская штука
                                          3. Насчёт персональных данных, корзины и прочих данных аккаунта не могу ничего сказать, так как один из сайтов статьёй которого хотел заручиться, работает на битриксе и лежит прям сейчас
                                          • 0
                                            1. Решается одним html тегом и стилями.
                                            2. Понятно что это не революционная технология, просто удобная вещь из коробки. не нужно самому заморачиваться, только ставишь в компоненте метку, ложить в кеш, или грузить после загрузки страницы. Мне кажется это очень удобно.
                                            Я не уверен, что ESI удобнее и практичнее композитного кеша, быстрее да, но мы же уже говорим про 6 мс., куда быстрее.
                                            3. Ну тогда закончим сей спор ))
                                            • 0
                                              1. Решается одним html тегом и стилями.

                                              Ой вей, как нехорошо нарушать лицензионное соглашение
                                              • 0
                                                Вы еще скажите, что фильмы с торрента не скачивали :)
                                                Не силен в юридических моментах, но там вроде ничего не сказано про визуальное оформление кнопки.
                                                Сказано, при условии «сохранения визуальной кнопки», а как и где вы её разместите, ваше дело. Они для этого даже предусмотрели проверку на пользовательский контейнер.
                                                Понятно, что мы не говорим про уровень Эльдорадо, там никто не будет хитрить и зажимать 300К )
                                                • 0
                                                  Собственно, не качаю, либо смотрю в кинотеатре, либо вообще не смотрю.
                                                  Но в этом году согрешил, Catch Me If You Can качнул, и чтоб совсем котолампово было, я его минут за 20 до вашего вопроса удалил
                                          • 0
                                            Она не генерируется за 6 мс, а выдается из кеша /bitrix/html_pages/<адрес_сайта>/<страница>.html
                                            • 0
                                              это понятно, я имел ввиду что страница загружается из мемкеша и отдается за 6 мс, выше так и написал.
                                  • 0
                                    Эльдорадо на битриксе, это 100% информация, так как я когда то работал на этом проекте :)
                                  • 0
                                    Эльдорадо вроде собирается мигрировать с битрикса на что-то java подобное. Буквально полгода назад набирали java-программистов для разработки магазина.
                                    • 0
                                      в итоге этот проект завершился провалом, поэтому пока все остается на битриксе, но действительно такая попытка была
                                  • 0
                                    В статье вы рекоммендуете изучать Symfony, Laravel, Yii. А что вы думаете про magento? может стоит попробовать его?
                                    • 0
                                      Как я писал выше, кроме битрикса мне пока не удалось плотно поработать с другими CMS.
                                      Могу точно сказать о Magento (на основании того, что я читал о ней в интернетах), что это достойная альтернатива битриксу, если вы хотите сделать интернет-магазин. Он в своем движке тоже использует компоненты Symfony. Один из минусов, который не дает развиваться этой платформе в России, это тот факт, что там пока не хватает готовых модулей интеграции с различными партнерскими, платежными, логистическими системами, CRM.

                                      Некоторые идеи в битриксе, насколько я знаю, перекачали как раз из Magento. Например конструктор скидок.
                                      • 0
                                        Magento — это пример того, как нужно делать современное коробочное решение для интернет-коммерции.
                                      • +1
                                        > В статье вы рекоммендуете изучать Symfony, Laravel, Yii. А что вы думаете про magento? может стоит попробовать его?

                                        думаю будет не корректно сранивать их. Symfony, Laravel, Yii это фреймворки, в то время как magento это система управления интернет-магазинами
                                        • +4
                                          Будет корректно сравнивать Magento и Bitrix.
                                          Я же рекомендовал изучать Symfony не столько, как альтернативу Битриксу, а как средство для получения более правильных навыков для веб-разработки. Да, на Symfony можно развернуть любое приложение, но это не всегда будет оправдано.
                                        • 0
                                          Magento требует не малый порог вхождения т.к. работает на ядре Zend(а) и имеет на первый взгляд сложную архитектуру.
                                          Еще весомый минус — документация. Если бы не stackoverflow, все было бы очень плохо.

                                          Я бы рекомендовал для начала начать именно с ZendFramework.
                                          • 0
                                            Узкоспециализированное решение, качество кода удовлетворительное, но хорошим его назвать нельзя.
                                            • 0
                                              Так продукт и позиционирует себя как e-commerce, а качества кода там на очень высоком уровне, в полном мере использует все преимущества ООП, и архитектура продумана для своих целей на строгую пятерку.
                                              • +1
                                                > Так продукт и позиционирует себя как e-commerce
                                                Мы говорим только о e-commerce?

                                                > а качества кода там на очень высоком уровне, в полном мере использует все преимущества ООП, и архитектура продумана для своих целей на строгую пятерку
                                                Строгая пятерка — это по десятибальной шкале?
                                                https://github.com/magento/magento2/blob/2.0/app/code/Magento/Catalog/Model/Product.php
                                                • +1
                                                  ахахаха)) посмотрел, согласен, мне казалось в двойке уже нет таких классов. В любом случае если сравнивать этот код с кодом Битрикса, то это 5 из 5. Можно ли лучше сделать? Безусловно, но это можно отнести к любому продукту.

                                                  > Мы говорим только о e-commerce?

                                                  Так просто Маджента под другие задачи не годиться :) Поэтому релевантно сравнить их можно только по этому критерию.
                                                  • 0
                                                    Зачетный конструктор класса)
                                            • 0
                                              Хорошо вы говорили по сути только про интернет магазины, а как насчет корпортала?
                                              • +1
                                                Корппортал = Битрикс24. Я немного коснулся этой темы, но и в корппортале тоже активно используются механизмы интернет-магазина. Например CRM отчасти использует возможности торгового каталога и ценообразования, а сделки — это есть ни что иное, как заказы.
                                                С Б24 мне приходилось работать, и работать с ним гораздо сложнее. Если вы хотите в Б24 доработать какой-то штатный механизм, то будьте готовы к тому, чтобы при следующем обновлении исправлять совместимость своих правок с новыми правками ядра, т.к. битрикс не следит за этим.

                                                В остальном, все написанное выше, также относится и к корппорталу, т.к. он также построен на основе тех же принципов а-ля MVC и Компоненты 2.0
                                                • 0
                                                  Позвольте узнать вы правите ядро? Или компоненты?
                                                  Опять же, в битриксе есть:
                                                  1. Сихнронизация с LDAP (да во фреймворках тоже есть), а тут из коробки
                                                  2. +- Чат корпоративный
                                                  3. Видеочат
                                                  И так далее.
                                                  Я не защищаю битрикс да местами он ужасен, но у него тоже есть своя ниша.
                                                  • 0
                                                    Нет, ядро править себе дороже выходит. В 99,9% случаев обходимся без правок ядра. На моей памяти это было лишь единожды, когда мне не хватило обработчика события еще до инициализации подключения основного кода битрикс.

                                                    Что касается стандартных компонентов, то сами компоненты в ядре мы тоже не правим.
                                                    Очень часто бывает проще написать свой собственный узкоспециализированный компонент, который решает узкую задачу и не зависит от других. В остальных случаях, если создание своего компонента неоправданно, то пользуемся теми средствами, которые предлагает битрикс, а это либо наследование компонентов (если они поддерживают), result_modifier.php, ну и в самом крайнем случае копипаста этого компонента и документированная доработка под версионным контролем.
                                                    • +1
                                                      Ниша есть.
                                                      А вы смотрели на другие продукты в этой нише? Он же не одинок там. Да, у него сильный маркетинг в РФ, но это же ничего не значит.
                                                      • 0
                                                        Можно примеры, на вскидку что видел:
                                                        Elma
                                                        Lement.PRO
                                                        и еще парочку.
                                                • 0
                                                  исходя из статьи я так понимаю отечественный рынок катится кое куда (не весь конечно же)… это прискорбно.
                                                  • +1
                                                    не теряйте надежду. В российском сегменте IT есть качественные продукты (например Nginx, Parallels). Я был бы очень рад, если разработчики Битрикса принимали во внимание все то, что о нем пишут и двигались навстречу к сообществу.
                                                    • +1
                                                      Мне кажется, что тут бесполезно пытаться влиять на разработчиков Битрикс. Их маркетинг работает в силу неграмотности и некомпетентности заказчиков. Ведь «спонсором» всего это безобразия являются заказчики. Поэтому можно и нужно «обрабатывать» заказчиков.

                                                      Допустим, вы умеете делать интернет-магазины «правильно» с помощью других технологий и методологий. Покажите заказчикам, насколько это выгоднее и со всех сторон лучше для них — и пусть выбирают вместо Битрикса ваше решение. Но вот парадокс — это маркетинговая задача. Потому как предпринимателей у нас мало, грамотных — исчезающе мало. И перебить условный рефлекс «1с в названии — значит все круто» сложно. Но можно.

                                                      Битрикс сейчас на коне — самая популярная коммерческая CMS. Когда у него появятся конкуренты, стремительно отжирающие долю пирога — вот тогда и задумаются. Правильно написано, что нормальная коммерческая компания гонится за прибылью. Если вы начнете своими действиями эту прибыль ощутимо снижать — это будет реально ощутимым стимулом, на который компании придется среагировать. Ну либо в долгосрочной перспективе она умрет.
                                                      • +4
                                                        Заказчик обычно приходит и говорит «Я вот купил лицензии на Битрикс, сделайте мне на нём сайт.» Всё. Можно либо сделать, либо отказаться целиком. Объяснять заказчику, что его уже развели — себе дороже, не поверит, обидится и решит, что это ты нихрена не знаешь и не умеешь — так ему морально проще.
                                                        • 0
                                                          Ну в том и сложность, чтобы перехватить заказчика на более раннем этапе. Или сами со временем научатся думать обо всем проекте целиком заранее (с учетом найма команды и т.п.), а не выкидывать деньги на ветер. Ну или хотя бы платить за консультации по стратегии и тактике решения бизнес-задач с помощью софта. Об этом и речь — заказчики в массе своей пока еще ужасно плохо ориентируются во всех этих технических делах.
                                                  • 0
                                                    юзайте webpack, который можно спокойно с битриксом подружить

                                                    Расскажите подробнее, пожалуйста.
                                                    Вы про локальный вебпак в ноде разработчика или о каком-то более плотном способе интеграции?

                                                    • +1
                                                      Ну да, про локальный вебпак. Все сырцы при разработке собираются вебпаком, и нативно подключаются битриксом с помощью какого-нибудь способа Asset-management'а (Классы Asset, или \CJSCore).
                                                      Точно также они собираются и при деплое. Благодаря плагину assets-webpack-plugin все собранные бандлы прокидываются в php с помощью связующего json файлика.

                                                      На самом деле у меня в задумках есть и более плотная интеграция, которая позволит хранить ресурсы там, где их предлагает хранить битрикс, т.е. непосредственно в компонентах. Но это пока в проекте.
                                                      • –1
                                                        То есть вы предлагаете, обычному магазину еще и nodejs тащить?
                                                        • 0
                                                          Почему бы нет? А в чем проблема? nodejs ставится в одну команду, зависимости из npm ставятся в одну команду. JS для большинства веб-разраотчиков не является преградой. Все хорошо и прекрасно.
                                                          Я же не заставляю вас использовать webpack. Вы можете точно также подключить Assetic из Symfony и пользоваться им, он на php. Но он что-то в последнее время не очень развивается, к сожалению.
                                                          • –2
                                                            nodejs легко ставится только большинство php разработчиков трудно поменять синхронный подход на асинхронный и не запутаться в колбеках
                                                            • 0
                                                              Это уже тонкости профессии
                                                              • +1
                                                                PHP-разработчику не нужно уметь программировать под nodejs, чтобы пользоваться такими инструментами как WebPack. Один раз только нужно написать конфиг (без программирования!) или попросить кого-то с этим помочь.
                                                              • 0
                                                                Assetic исключен из третей версии, предлагается использовать сборщики Webpack, Gulp, etc.
                                                              • 0
                                                                Он ставится на машину разработчика, на машину хостинга иногда приемлемо только итоговый файл ставить. Так что строго говоря на сервере node.js не обязателен. Но лучше конечно делать по человечески, как mmjurov написал.
                                                          • 0
                                                            сколько вы берёте за такие магазины?)
                                                            • +7
                                                              Сколько бы ни брали, все равно этого будет недостаточно для того, чтобы покрыть все то, что описано в этой статье :)
                                                              • 0
                                                                Зачем тогда брать?
                                                                У меня один коллега вечно обижен на всё, всё время ненавидит проект, над которым работает. На резонный вопрос — почему ты не уйдёшь, он ответил так: «пока мне платят столько, сколько я хочу, я никуда не пойду, это меня устраивает более чем».
                                                                Вывод только один. Суммы сделок покрывают то, что описано в этой статье.
                                                            • +5
                                                              Одно слово «Битрикс» у меня всегда вызывало раздражение, а теперь я знаю почему.
                                                              • +11
                                                                5 лет работать с ЭТИМ? Да вы очень терпеливый!
                                                                • +12
                                                                  А еще мазохист, да )
                                                                  • 0
                                                                    Жить захочешь — не так раскорячишься :) Вот, например, человек сам себе стандартную библиотеку написал code.google.com/archive/p/lib1c
                                                                    Первый раз вижу, что кто-то использует английский синтаксис в 1С pastebin.com/iRqHinJu
                                                                  • +3
                                                                    Если вы с помощью визуального редактора поменяете параметры комплексного компонента, то файлик с настройками адресации (urlrewrite.php) будет перезаписан системой. Причем, если вы вдруг что-то неправильно там написали для других страниц обязательно что-то сломается без какого-либо предупреждения. На практике это может привести к потере работоспособности целых разделов сайта.


                                                                    Ох как я плевался от этого, причем столкнулся в первое же знакомство с Битрикс…
                                                                    • +6
                                                                      Да, ваша статья пожалуй самая объективная из написанных на днях (ссылки на них вы приводили) — виден опыт работы как с «пациентом» так и с другими продуктами (популярными фреймворкам).
                                                                      Если у вас нет выбора, то изучайте Битрикс, но в свободное время лучше все-таки пытаться погрузиться в мир фреймворков, чтобы поставить мозги на место.

                                                                      Разве существуют такие ситуации когда нет выбора? Рабство и крепостное право вроде как отменили, а работу всегда можно поменять…
                                                                      • +3
                                                                        Я думаю да, существуют. Например в регионах, где Битрикс монополист. Для новичка может быть сложно переехать из региона в столицу, чтобы найти работу для начинающего в сфере веба. А работу с Битриксом найти в регионе не очень то сложно, т.к. берут всех подряд, низкий порог входа, можно очень быстро обучить.
                                                                        • 0
                                                                          Возможно и так, сугубо для тех кто не готов прыгать сразу «в овраг», сменить работу будет не просто. В принципе даже на битриксе, человек имеющий опыт (а точней знания) работы с адекватными MVC-архитектурами будет продуцировать вполне качественный код, но вопрос остается открыт — почему такого «качественного» кода до сих пор с огнем найти сложно, а само «ядро» битрикса и его документация пестрит перлами уровня junior и «программируем как умеем»? Из этого вытекает что над «битриксом» работают лишь студенты или те, кто не желает развиваться?
                                                                          • +3

                                                                            Я, например, сейчас живу в регионе (Челябинск), и тоже в начале своего пути столкнулся с битрикс и у меня остались точно такие же ощущения от него. Но вот я перешел на Python/Django и нарадоваться не могу и совершенно спокойно нашел удаленную работу, т.е. хочу сказать переезжать в столицу не обязательно.

                                                                            • 0
                                                                              Вы счастливчик, и видимо, обладаете хорошим чутьем и способностями к самообучению, что является очень ценным качеством!
                                                                              Большинство начинающих, которые приходят к нам в компанию, не обладают не то что навыками Junior'а, они и просто не могут простейшую функцию написать. Такие люди особо не нужны никому на удаленную работу, т.к. их обучением заниматься удаленно банально физически невозможно.
                                                                              • 0
                                                                                Я, наверное, странный разработчик. Пишу под одну русскую CMS в основном, которой реальный конкурент разве что PHP-Fusion (но по коду он хуже: множество точек входа, строение — перемешка PHP. if...ifelse и HTML). Остальные CMS не интересны (у WP примерно такая же мешанина, Joomla тоже к себе не располагает). Смотрел Symfony2, но она как-то монструозна. В ней можно разобраться, но не понимаю для чего все эти навороты с 4 разными видами конфигов.

                                                                                В итоге сейчас тоже ухожу в сторону Python, правда взял Flask — легкий и гибкий, это мне нравится. Django мне понравилась (в отличии от Symfony2 она более логичная, нет этих 100500 наследований и неймспейсов). Но для текущего проекта ее не стал брать, ибо кажется, что она заточена под определенные задачи (так ли это на ваш взгляд?), для чего и поставляются в ней «батарейки» (готовая админка, правда с русским у нее не очень. модель пользователя, ORM и прочее). Вот типовое на ней вроде магазина, блога делать за милую душу: модели, несколько вьюх, сказать админке что и как выводить — сайт готов. А если нужен профиль пользователя сложный, с разными видами авторизации — тут веселее (до какой-то версии с этим были трудности, как я помню).

                                                                                Может где-то и плох мой подход, но я предпочитаю писать специализированное решение на чем-то минимальном, чем переопределять и перепилировать крупный фреймворк. Но в этом точно есть плюс: решение получается более легкое, производительное, заточено под требуемые задачи. Минус в том, что архитектуру нужно строить самому и тут можно накосячить…
                                                                                • 0
                                                                                  symfony2, zf2, django, ruby on rails, всё это mvc фреймворке и очень похоже. У php есть композер и такие же готовые решения, и процесс разработки схожий, подымаете проэкт -> устанавливаете нужные компоненты -> определяете свои view и всё готово.
                                                                                  • 0
                                                                                    Знаю про все это, просто не понимаю чего это Symfony2 все боготворят и с радостью используют (я вижу в ней академического монстра). Видимо, проф разработка на PHP — не мое…
                                                                          • +2
                                                                            1. Не скажу за всю разработку на Bitrix, но то, что в коде мешанина — это да! Плюс к тому же на прошлой работе сайт был сдан, видимо, в спешке, так что пришлось погрузиться и в документацию, которая тоже «впечатлила». Вроде и чувствуется, что люди стараются всё разжевать, но сама монструозность и сложность системы не дает в короткие сроки усвоить объём всех знаний.

                                                                            2. В общем-то п.1 не был моей основной работой, а больше приходилось работать с контентом. Соглашусь с автором, что создание разделов и страниц не всегда понятно и однозначно.

                                                                            3. Уважаемый mmjurov и комментирующие, что можете сказать по поводу встроенной «Проактивной защиты»? Неоднократно приходилось вычищать сайты на других CMS (а после обложить либо chmod 555, либо прочими способами защиты), но сам видел и логи «Проактивной защиты», и не имел в этом плане проблем с Битриксом. Как обстоят дела в фреймворках из коробки?
                                                                            • +1
                                                                              Могу точно сказать, что проактивная защита решает те задачи, которые на нее возлагаются. Как мне кажется, этот модуль создавался для того, чтобы покрыть недостаточный уровень защиты того кода, который производят разработчики платформы. Мой коллега, пентестер Sudo, тестировал много разных сайтов, и битрикс оказывался наиболее защищенным по его статистике.
                                                                              Но за все приходится платить, и модуль проактивной защиты добавляет свои сотые, а то и десятые доли секунды к общему показателю скорости генерации страницы.
                                                                              • 0
                                                                                Да, имхо так сказать:
                                                                                Какой бы ни бил Битрикс, но обычно ничего страшнее XSS'ок не обнаруживается. Я тут не считаю за уязвимости phpinfo (и т.п.) на «секретном» url, смотрящие наружу репозитории и dev-ресурсы, слабые пароли или уязвимые версии установленного ПО — все это не проблема самого Битрикса, хотя и приводит в итоге к его компрометации.
                                                                                Я думаю, на это есть несколько причин:
                                                                                • Наличие уже упомянутого проактивного фильтра в базовой поставке
                                                                                • Изначально платный продукт, из-за чего его вряд ли выберет подавляющее большинство компаний без выделенного бюджета «на сайт»
                                                                                • Малая распространенность самой CMS за пределами СНГ
                                                                                • +1
                                                                                  Уточнение по последнему пункту списка:
                                                                                  Имею в виду, что по этой причине, в частности, в паблике практически нет описаний уязвимостей и эксплоитов под Битрикс

                                                                                  Например:
                                                                                  https://www.exploit-db.com/search/?action=search&description=bitrix
                                                                                  • +1
                                                                                    Знаю уязвимость в одном из «инструментов для битрикса» которые дают скачать в документации. Позволяет получить полные права на сайт. Подвержено очень много сайтов. Но в документации так и написано, что очень опасно оставлять такие скрипты на сайте. Это уже на совести разработчиков.
                                                                              • +1
                                                                                TL;DR — проактивная защита работает хорошо, но не является панацеей. С фреймворками ситуация всё равно лучше. Про Joomla-только печально вздохнуть.

                                                                                У битрикса проблемы с уязвимостями видел (не связаны с проактивной защитой). Просто по FTP вирусом заливается файл, который спам шлёт. Тут проблема битрикса только в том, что чистить сложно и подход к работе связан с FTP, часто этот FTP для доработок выделяется заказчику. Ещё одна причина взломов популярных CMS — как правило, (не все, но многие) вирусы ищут файлы «index.php» и в них вклиниваются. У ООП фреймворков файлы называются Dispatcher.php или UsersController.php. Это на небольшую йоту понижает вероятность заражения.

                                                                                Joomla, Worpdress тоже самое, а также взламываются из за уязвимостей. Гораздо чаще битрикса. Битрикс, взломанный изнутри через уязвимость не видел никогда.

                                                                                Взломанных сайтов на Yii и фреймворков не видел никогда, ни в каком виде. Видимо, сказывается подход, например, код отдельно, публичная директория отдельно, загрузка кода через git, что легко палит новые файлы, доступ к базе данных и обработка POST запросов только через точно отмеренный REST, и в целом как то легче живется с этим.
                                                                                • 0
                                                                                  В Yii нет index.php?
                                                                                  Я бы сказал скорее правильные выставленные права, потому как обычно при установке битрикса делают chmod -R 777 ;)
                                                                                  • 0
                                                                                    Их два. При этом оба — не в public директории. Т.е. даже после заражения вызвать их напрямую не получится.
                                                                                    Заголовок спойлера
                                                                                    { yii2-master }  » find . -name  'index.php'
                                                                                    ./framework/requirements/views/console/index.php
                                                                                    ./framework/requirements/views/web/index.php
                                                                                    



                                                                                    Два — не 1000. Меньше вероятность заразиться. А вирусы, которые лезут только в index.php, есть. Ясное дело, не панацея, но вероятность уменьшит — вирус просто не находит знакомых для него названий файлов. Многие находят папку components (джумловскую) и в них лезут, например.

                                                                                    Ну и да, выставленные права — один из аспектов подхода и экосистемы.
                                                                                    • 0
                                                                                      в паблике у yii2 есть index.php
                                                                                      но, ничто не мешает у www-data забрать права на запись туда…
                                                                              • +1
                                                                                Я прочитал треть статьи, где еще что-то понятно простому человеку, сам параллельно решил в это время сделать резервную копию своего магазина на Битриксе и он видимо понял что на соседней вкладке его ругают и решил не загружаться)

                                                                                Мне, как простому пользователю, обидно что они до сих пор еще не сделали простую выдачу поиска с картинками из коробки, может хоть здесь прочитают, а то прямые обращения не сильно помогают.
                                                                                • +4
                                                                                  Спасибо за статью, приятно было читать. Сам работаю с Битриксом уже 5 лет и готов подписаться под каждым словом. За это время стал тимлидом и попал в «золотую клетку», платят хорошо и стабильно, а работать с Битриксом — это боль каждый день.
                                                                                  • 0
                                                                                    У меня та же фигня была, правда я с Sharepoint работал, там боли поменьше, но все равно до «Best Practice» и «Как уменьшить скорость загрузки страницы с 700 до 400мс» далеко. Специально искал место, где я бы смог развиваться как разработчик, ибо реально стал чувствовать, что конечно и платят хорошо, и коллектив замечательный, но я уже давно не развиваюсь. В результате нашел, первые пару недель было довольно тяжело, новый коллектив, скрам вместо водопада — вот это все, весьма непривычно. Однако сейчас птички поют, все прекрасно, вместо боли с шарком изучаю ASP.Net Core 1.0, запускаю кроссплатформенное приложение на шарпе под линуксы, использую свежайший фреймворк… В общем, изначальные сомнения «стоит ли уходить из зон комфорта» полностью растворились. Стоит обязательно, если вы чувствуете, что не развиваетесь, потому что отсутствие развития есть деградация, вспоминаем Алису в стране чудес. Так что стоит бежать еще быстрее, и все получится.
                                                                                  • 0
                                                                                    Продолжаю убеждаться в том, что правильно советовал мелким магазинчикам не мучаться, а выбирать Wordpress + модуль онлайн магазина, которые развернуть можно в один клик на любом захудалом копеечном шаредхостинге, ибо магента сложна, битрикс смердит — на текущем продакшене одного проекта, который я переделываю с нуля на рельсах, цены надо менять минимум в 4х местах, если повезёт, и автор сего поделия уже сам не помнит, как это всё исправлять, хотя 10 лет назад это было огого!
                                                                                    • 0
                                                                                      В контексте п.3 этого комментария не могли бы подсказать, какие плагины безопасности и способы защиты вы используете для WP?
                                                                                      • 0
                                                                                        Я не знаю, честно, что там используют клиенты в своих сайтах — каждый крутит как умеет, если умеет — у меня их сотни на каждом сервере.
                                                                                        Для тех, кто спрашивал — я рекомендовал как минимум wp-admin закрыть htaccess'ом на айпишники офиса/дома,
                                                                                        В панели управления хостингом, которую пишу сам — имею несколько идей для помощи клиентам, а так — я и php не очень совместимы, поэтому не подскажу.
                                                                                        Да и сам в феврале сменил wp на jekyll
                                                                                      • 0
                                                                                        Значит в WordPress с ООП и архитектурой стало все хорошо ?:)
                                                                                        А то что там миллион плагинов для кэширования надо поставить это так тонкость.
                                                                                        • +1
                                                                                          У меня собственный хомяк на WP грузится быстрее, чем корп. страница местной 1С франчайзи (на 1С-Битрикс) примерно в 10 раз. Генерация быстрее также в разы. При этом, у них сайт делали сертифицированные специалисты (тм), крутится он на нехилом дедике (тм), а у меня это VPSка за 5 у.е./месяц, на которой живет целая куча сервисов и сайтов, настроенная моей левой пяткой.
                                                                                          • 0
                                                                                            Насчёт ООП — я и php совместим на уровне phpinfo(), а вообще то, что я вижу по своим хостинг проектам и дружественным — сам по себе вордпресс стал очень и очень неплох — в 90% случаев ломают или криво написанные плагины/темы или это украденные плагины/темы с пиратских сайтов с закладками.
                                                                                        • 0
                                                                                          Про Битрикс правда написана, но где взять альтернативу редакции Бизнес по функционалу с грамотной архитектурой? Подобную статью про любую CMS можно написать.

                                                                                          Качественную самописную CMS не каждый интернет-магазин себе сможет позволить, да и поддержка замыкается на разработчика этого решения.
                                                                                          • +2
                                                                                            … да и поддержка замыкается на разработчика этого решения.

                                                                                            Так грамотная архитектура и есть гарантия не зависимости от разработчика.
                                                                                            • 0
                                                                                              > но где взять альтернативу редакции Бизнес по функционалу с грамотной архитектурой?

                                                                                              Тут придется выбрать либо грамотную архитектуру, либо привлекательную цену лицензии Битрекс Бизнес. Так что вопрос не в «где» взять, а в том сколько это будет стоить.
                                                                                              • 0
                                                                                                Самописная CMS — это CMS-от-студии или написать CMS с нуля для конкретного проекта? Потому что второе — это изврат извините, это лапша, говнокод и уязвимости, а первое — дешевле аналогичного функционала на битриксе зачастую, по наблюдениям.
                                                                                              • +2
                                                                                                «К слову, дорабатывать компоненты в коробочной версии Б24 — та еще задачка. Компоненты, которые генерируют js код, который с помощью ajax обращается к php коду, который в ответ генерирует html+js. Это адовая смесь, в которую очень не хочется погружаться.» — полностью поддерживаю. Пишу в коробочной версии, у некоторых компонентов просто нереально изменить вид, потому что это js, который прописан где бы вы думали? — ну конечно в классах модуля.
                                                                                                Отдельная тема — это обновления и техподдержка. После обновления перестал переключаться месяц в стандартном компоненте «Календарь». Ответили — мы знаем, мы делаем. Они тупо забыли засунуть в компонент код js. Жду второй месяц, пользователи нервничают.
                                                                                                Понадобился компонент выводящий таблицы, есть такой стандартный системный компонент grid, в принципе неплохой. Вся его беда, в том, что он системный — это значит, что на него нет документации. Официальное обоснование Битрикса — если вы разработчик, вы и так разберетесь, а если нет, то сломаете. Поэтому документацию grida мы писать не будем.
                                                                                                • +3
                                                                                                  По поводу введения новых красивых фич. К сожалению ссылки не сохранил, но нередки подобные темы на оф. форуме:

                                                                                                  Посетитель: Что-то не работает фича ххх. Просто перестала работать. странно, ведь в релизе обещали (ссылка на обещания)
                                                                                                  Посетитель: Что-то ссылку закрыли… В бета-версии вроде работала…
                                                                                                  Админ: Этой фичи больше нет.

                                                                                                  Посетитель1: бла-бла… Кастомные статусы заказов коряво подгружаются на сайт
                                                                                                  Админ: Разработчикам сообщено, скоро поправят.
                                                                                                  [9 месяцев спустя]
                                                                                                  Посетитель1: До сих пор не работает.
                                                                                                  Админ: Уже поправили, в ближайшем релизе будет.
                                                                                                  [ЕЩЕ 9 месяцев спустя]
                                                                                                  Посетитель2: Как там что? Уже работает?
                                                                                                  Посетитель3: До сих пор нет.
                                                                                                  • +1
                                                                                                    Это не «не редки», это норма для битрикса. Ощущение, что он хорошо рванул вперёд года четыре назад, а потом его убили маркетологи, которые быстро внушили программистам, что нужно деньги зарабатывать, а не мир к лучшему менять. В результате сначала были заброшены все направления, кроме интернет-магазина, а потом всё, кроме bitrix24.
                                                                                                    А сейчас Рыжиков без стеснения говорит, что на рынке победят только те продукты, которые выпускаются недоработанными.
                                                                                                • +6
                                                                                                  Мне страшно писать комментарии к этим статьям. Ладно, мне не страшно, я readonly. Очень понравился обзор, действительно наполнен пруфами. Особенно понравилось про интеграцию с 1С. Могу лишь добавить, что в том случае когда бизнеса у заказчика ещё нету и он покупает 2 коробки (1С Битрикс, 1С Предприятие), то интегрировать всё равно не получится. А знаете почему? Потому, что у заказчика как и у 90% реселлеров конкретный продукт идентифицируется по связке артикул+бренд, а не просто по артикулу. Я разговаривал с тех.поддержкой 1С, отгадайте что мне сказали? Мне сказали вести учёт по баркодам либо дописать обмен так, чтобы там присутствовали необходимые параметры. Нет, дописать не скрипт в битриксе, это само собой, не подумайте, что всё так просто, дописать в 1С Предприятие обмен. На языке мордора (ЕСЛИ, ТО, ИЛИ и.т.п). И тогда, в моём блин очень не стандартном случае всё будет работать. В общем есть бизнес, нету бизнеса — придётся заплатить интегратору + web-разработчику, либо поломать голову и выучить мордор-лэнгвич в 100% случаев. И нет, вы не исключение. И я кстати не знаю ещё что хуже, когда уже есть боевая 1С или когда знакомство с интегратором ещё предстоит…
                                                                                                  • 0
                                                                                                    Я тоже readonly. У 1С Предприятия много недостатков, но относить к ним русский язык в модулях как то странно.
                                                                                                    Насчет «выучить» там в общем то практически стандартный бейсик.

                                                                                                    Например что тут не понятно https://gist.github.com/anonymous/73fead42abf2b25e46b1c5d9ab4a8823? Непривычно? Вполне может быть но за несколько дней можно привыкнуть. Я пробовал писать используя английский синтаксис но довольно не просто переводить «бизнес термины» на английский язык:
                                                                                                    ВВод показаний ПУ(Приборы учета), Закрытие дебиторской задолженности, МакетФормыНДФЛ2, АдресныеСокращения, АдресныйКлассификаторУСЗН, Бригады ТО (Тех обслуживания)

                                                                                                    Да для всех этих терминов можно подобрать аналоги на английском, только зачем?

                                                                                                    Я в целом понимаю почему в программировании принято использовать английский (это сразу понимаешь когда читаешь например код C# написанный на французом) т.к. при создании продуктов рассчитанных на WORLD не красиво использовать свой язык т.к. другим будет не понятно.

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

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

                                                                                                    В общем на мой взгляд проблема «русского» языка преувеличена.

                                                                                                    • 0
                                                                                                      Очень приятно, два удивительно ленивых (readonly) человека (вы с 2011, я с 2012) встретили друг друга. Я не то чтобы совсем против русского языка в коде бухгалтерии для России. Скажем так, я больше против бейсика. Крути не крути — язык устарел. А когда он ещё и кириллицей, ну извините… Язык мордора получается. И Вы из года в год пишите на этом языке. Я так понимаю, что это больше нигде и не может пригодиться. Вот представьте: случилось чудо, компания 1С перешла на Ruby или Python и Вы будете не программистом бейсик-кириллицы, а программистом современного, востребованного языка. Вот я мыслю именно в таком ключе. Деньги деньгами, а инвестиции в себя (в знания) никто не отменял.
                                                                                                  • 0
                                                                                                    По поводу интеграции с 1С вспоминается случай. Года 3 назад одному клиенту ребята обещали сделать супер-пупер-магазин на Битриксе и даже с 1Ской его связать. Полгода попыхтели и слились со словами «у вас гранаты не той системы 1С v7, мы и Битрикс с такой не умеем работать»…
                                                                                                    • 0
                                                                                                      у нас даже близко не 1С, а Access, однако сопрягли через CommerceML2 используемый в 1С с Битриксом и все хорошо.
                                                                                                      • 0
                                                                                                        Ну так вы не мне, а тем ребятам расскажите :)
                                                                                                    • +2
                                                                                                      Есть 2 интернет магазина, которые я не люблю посещать из-за того что они дико тормозят, бывает по 10 секунд ждешь страницу и это не во время диких распродаж(во время распродаж сделать заказ там — настоящий чалендж), это штатная работа магазина. Эльдорадо и Дочки-Сыночки, сейчас проверил — оба на Битриксе.
                                                                                                      • 0
                                                                                                        Ну не знаю, эльдорадо довольно быстрый сайт, а вот есть юлмарт. Только мобильная версия и выручает.
                                                                                                        • 0
                                                                                                          Юлмарт иногда тормозит, но это редкость, я бы не назвал его быстрым, но скорость удовлетворительная, хотелось бы шустрее. А вот у Эльдорадо страница со списком товаров после применения фильтра грузится секунд 15 и это не время полной загрузки с картинками и скриптами, это время между запросом страницы и началом её отдачи. Кешированные ясное дело быстро отдаются.
                                                                                                      • +6
                                                                                                        Совсем недавно один мой старый давнишний клиент из штатов, после того как попробовал bitrix24 в облаке, поставил меня перед фактом, что, мол, купил standalone версию для установки на сервер, мол, хочу точно такой-же но свой — на своем хостинге. Мол, нужно все тоже самое, но чу-у-у-у-ть чу-у-у-у-ть подправить под свои нужды.
                                                                                                        Зная, что клиент под чуть-чуть подправить обычно имеет ввиду довольно значительные изменения, попытался его отговорить — всё безуспешно. Клиент уже купил эту штуку, и советоваться с технарями в его планы не входило с самого начала. Ну да ладно, купил клиент этот Битрикс24 — будем работать — так как я работаю с Клиентом через Upwork на почасовой ставке — много работы это всегда много денег, а клиент всегда хорошо платил, кто мы такие чтобы считать деньги клиента. Причем Битрикс 24 это не Битрикс сайт — это полный фарш и стоит он значительных денег. Начали работу.
                                                                                                        Вообще говоря, с самого начала мне бы нужно было занять более настойчивую позицию и убедить что затея не стоит усилий — Битрикс это не то что легко поддается модификации. Но, имея за плечами значительный опыт разработки под Web, мне захотелось познакомиться и с Битрикс тоже. В целом, да, все так как написано в статье, много кода который не просто с душком, а который давно умер и воняет…
                                                                                                        Что можно сказать, я, конечно, видел код и похуже. Сделать простую модификацию какой нибудь ерунды выливается в то, что измененными оказываются десятки файлов. Работать с кодом вполне можно, код простой и понятный, но его много и написан он разными программистами в разное время. Если вдруг вы решили накатить обновление — все ваши изменения оказываются затертыми и надо всё делать заново, использование git частично решает проблему, но не полностью.
                                                                                                        Кроме того, прежде чем купить Битрикс — продавцы уверяли что продукт на сто процентов open source. Однако, это оказалось наглой ложью — под open source они имели в виду что php код серверной части не обфускирован (хотя некоторые основные файлы все же оказались обфускированными). К исходным кодам, разумеется, не прилагалось никаких юнит тестов. Клиенты под мобильные платформы и настольные компьютеры идут без исходных кодов и заказчику, который купил лицензию они не предоставляются. Работают эти приложения довольно паршиво, у клиента из четырех компьютеров видеозвонки заработали лишь на одном — а поскольку исходников нет — исправить проблемы самостоятельно не представляется возможным.
                                                                                                        В результате после года безуспешных попыток модифицировать Битрикс «под себя» решили от этой затеи отказаться, уж слишком долго и дорого выходил проект.
                                                                                                        Так как клиент был старым и проверенным, ко мне вопросов у него нет — я с самого начала его информировал а таком финале, но зато теперь я получил незабываемый опыт «работы» с системой и могу аргументированно объяснять клиенту почему ему не нужен Битрикс. До этого я мог только ссылаться на чужой опыт.
                                                                                                        • +1
                                                                                                          «Если вдруг вы решили накатить обновление — все ваши изменения оказываются затертыми и надо всё делать заново, использование git частично решает проблему, но не полностью. »
                                                                                                          Данное высказывание говорит о том что вы абсолютно не знакомы с продуктом, боюсь даже подумать что вы там год модифицировали