показать полностью
Пользователь 17890  -> Всем
13 Июль, 2024 19:24
форк известного инструмента bxApiDocs с обновлениями
Запустили свой fork с обновлениями подсказок ядра bitrix для IDE
https://github.com/hipot-studio/bxApiDocs
UPD. Про решение bxApiDocs писали много где, напр.
https://dev.1c-bitrix.ru/community/web...blog/9382/
или https://dev.1c-bitrix.ru/search/?q=bxApiDocs
Теги:
показать полностью
Пользователь 17890  -> Всем
3 Март, 2020 15:00
частая ошибка, может у кого есть уже готовый кейс решения?
PHP Fatal error:  ob_end_clean(): Cannot use output buffering in output buffering display handlers in /home/bitrix/ext_www/_____________/bitrix/modules/main/classes/general/main.php on line 3113

UPD ошибка оказалась в:
  • использовании функций буферизации ob_* в шаблоне компонента breadcrumb
  • в init.php подключался файл, где использовался $cache->StartDataCache, который использует буферизацию (ob_start и ob_end_clean).
  • в событии OnEndBufferContent нельзя использовать конструкции вида ob_start(), вызывающую ошибку при этом: ob_start(): Cannot use output buffering in output buffering display handlers (0)  - а следовательно в обработчике событий OnEndBufferContent не допускается использовать старый d0 класс CPHPCache, который вызывает ob_start() у себя под капотом.  

    Решение - использовать класс d7 \Bitrix\Main\Data\Cache с вызовом \Bitrix\Main\Data\Cache::noOutput() перед запуском \Bitrix\Main\Data\Cache::startDataCache()  ну или использовать хак-магию доступа к приватному члену класса CPHPCache
Чебан Валерий
И еще один кейс этой же ошибки:

Обратите внимание, что в событии OnEndBufferContent нельзя использовать конструкции вида ob_start(), вызывающую ошибку при этом:
ob_start(): Cannot use output buffering in output buffering display handlers (0)

а следовательно в обработчике событий OnEndBufferContent не допускается использовать старый d0 класс CPHPCache, который вызывает ob_start() у себя под капотом.

Решение - использовать класс d7 \Bitrix\Main\Data\Cache с вызовом \Bitrix\Main\Data\Cache::noOutput() перед запуском \Bitrix\Main\Data\Cache::startDataCache()

ну или использовать хак-магию доступа к приватному члену класса CPHPCache
1 Ещё
Рыскулов Талант
Чебан Валерий, здравствуйте, можно подробнее узнать где применить этот класс d7?
0 Ещё
Чебан Валерий
Рыскулов Талант, отвечаю в контексте вопроса про кэширование и ошибку "Cannot use output buffering in output buffering display handlers"

Примерно вот так если вы используете старый класс d0 для кэширования можно отменить "выводимый-буферный" кэш:

// код в init.php

$oCache = new CPHPCache();

// отключаем буфер ob_* для кэша для старого кода
Hipot\BitrixUtils\PhpCacher::noOutputCacheD0($oCache);

if ($oCache->StartDataCache($CACHE_TIME, $CACHE_ID, $cachePath)) {
   // ...

   if (count($arResult) == 0) {
      $oCache->AbortDataCache();
   } else {
      $oCache->EndDataCache(["arResult" => $arResult]);
   }
} else {
   $arVars      = $oCache->GetVars();
   $arResult   = $arVars["arResult"];
}

// или лучше сразу использовать d7

$cache = Bitrix\Main\Data\Cache::createInstance();
$cache->noOutput(); // отключаем буфер ob_* для кэша
if ($cache->initCache($cacheTime, $cacheId, $cacheDir)) {
   $result = $cache->getVars();
} elseif ($cache->startDataCache()) {
   $result = [];
   // ...
   if ($isInvalid) {
      $cache->abortDataCache();
   } else {
      // ...
      $cache->endDataCache($result);
   }
}


А про класс PhpCacher можно почитать в репозитории.
0 Ещё
показать полностью
Пользователь 17890  -> Всем
14 Август, 2023 0:20
Добавление плейсхолдеров из почтового события SALE_NEW_ORDER в почтовые события смены статуса заказа SALE_STATUS_CHANGED_X
Задача тривиальная и решений у нее много. В основном все "по старинке" дергают событие поступления почтового события и в нем что-то делают с массивом письма шаблона (дописывают недостающие и/или модифицируют имеющиеся).

Я набросал решение с класса \Bitrix\Sale\Notify, который не особо документирован, но с него накопировал кода под заполнение плейсхолдеров, как в шаблоне SALE_NEW_ORDER.

Ниже код, как пример использования, собрал на "быструю руку":

// init.php

use Bitrix\Main\Loader;
use Bitrix\Main;
use Bitrix\Main\EventManager;
use Bitrix\Main\Application;
use Bitrix\Sale\Basket;
use Bitrix\Sale\Order;
use Bitrix\Sale\OrderBase;
use Bitrix\Sale\Helpers;
use Bitrix\Sale\PropertyValueCollection;

$eventManager = EventManager::getInstance();
$request      = Application::getInstance()->getContext()->getRequest();

$eventManager->addEventHandler('sale', /*\Bitrix\Sale\Notify::EVENT_ORDER_STATUS_SEND_EMAIL= */ 'OnOrderStatusSendEmail',
   static function ($orderId, &$eventName, &$fields, $statusId) use ($request) {
      $entity = Bitrix\Sale\Order::load($orderId);

      $separator = "<br/>";

      $eventName = Bitrix\Sale\Notify::EVENT_DEFAULT_STATUS_CHANGED_ID . $statusId;

      $filter = array(
         "EVENT_NAME" => $eventName,
         'ACTIVE' => 'Y',
      );

      if ($entity instanceof OrderBase) {
         $filter['SITE_ID'] = $entity->getSiteId();
      } elseif (defined('SITE_ID') && SITE_ID != '') {
         $filter['SITE_ID'] = SITE_ID;
      }

      $res = \CEventMessage::GetList('', '', $filter);
      if ($eventMessage = $res->Fetch()) {
         if ($eventMessage['BODY_TYPE'] == 'text') {
            $separator = "\n";
         }
      }

      $basketList = '';
      /** @var Basket $basket */
      $basket = $entity->getBasket();
      if ($basket) {
         $basketTextList = $basket->getListOfFormatText();
         if (!empty($basketTextList)) {
            foreach ($basketTextList as $basketItemCode => $basketItemData) {
               $basketList .= $basketItemData . $separator;
            }
         }
      }

      $getUserEmail = static function (Order $order) {
         $userEmail = "";

         if (empty($userEmail)) {
            /** @var PropertyValueCollection $propertyCollection */
            if ($propertyCollection = $order->getPropertyCollection()) {
               if ($propUserEmail = $propertyCollection->getUserEmail()) {
                  $userEmail = $propUserEmail->getValue();
               }
            }
         }

         if (empty($userEmail)) {
            $userRes = Main\UserTable::getList(array(
               'select' => array('ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'EMAIL'),
               'filter' => array('=ID' => $order->getUserId()),
            ));
            if ($userData = $userRes->fetch()) {
               $userEmail = $userData['EMAIL'];
            }
         }

         return $userEmail;
      };

      $getUserName = static function (Order $order) {
         $userName = "";

         if (empty($userName)) {
            /** @var PropertyValueCollection $propertyCollection */
            if ($propertyCollection = $order->getPropertyCollection()) {
               if ($propPayerName = $propertyCollection->getPayerName()) {
                  $userName = $propPayerName->getValue();
               }
            }
         }

         if (empty($userName)) {
            $userRes = Main\UserTable::getList(array(
               'select' => array('ID', 'LOGIN', 'NAME', 'LAST_NAME', 'SECOND_NAME', 'EMAIL'),
               'filter' => array('=ID' => $order->getUserId()),
            ));
            if ($userData = $userRes->fetch()) {
               $userData['PAYER_NAME'] = \CUser::FormatName(
                  \CSite::GetNameFormat(null, $order->getSiteId()), $userData, true
               );
               $userName               = $userData['PAYER_NAME'];
            }
         }

         return $userName;
      };

      $fields = array_merge($fields, array(
         "ORDER_ID" => $entity->getField("ACCOUNT_NUMBER"),
         "ORDER_REAL_ID" => $entity->getField("ID"),
         "ORDER_ACCOUNT_NUMBER_ENCODE" => urlencode(urlencode($entity->getField("ACCOUNT_NUMBER"))),
         "ORDER_DATE" => $entity->getDateInsert()->toString(),
         "ORDER_USER" => $getUserName($entity),
         "PRICE" => SaleFormatCurrency($entity->getPrice(), $entity->getCurrency()),
         "BCC" => Main\Config\Option::get("sale", "order_email", 
                           "order@" . $request->getServer()->getServerName()),
         "EMAIL" => $getUserEmail($entity),
         "ORDER_LIST" => $basketList,
         "SALE_EMAIL" => Main\Config\Option::get("sale", "order_email", 
                           "order@" . $request->getServer()->getServerName()),
         "DELIVERY_PRICE" => $entity->getDeliveryPrice(),
         "ORDER_PUBLIC_URL" => Helpers\Order::isAllowGuestView($entity) ? Helpers\Order::getPublicLink($entity) : ""
      ));

      return true;
   }
);


hipot studio — разработка, поддержка и сопровождение интернет-проектов  
показать полностью
Пользователь 17890  -> Всем
10 Ноябрь, 2013 21:18
Короткие разыменования в PHP, удобные записи в виде $foo->bar()[0] можно ли?
В статье будут рассмотрены темы:
Развитие синтаксиса PHP
Будет дан ответ на аксиому "почему только 5.3, не выше?" и что из нового синтаксиса можно.
Вопрос совместимости, "исторические хвосты"

Мы в данный момент работаем по версии 5.3. Ниже я описываю новые улучшения в 5.4 и 5.5. К сожалению, возможности из этих версий пока использовать НЕЛЬЗЯ. Можно использовать только возможности 5.3.

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

Чебан Валерий
привет из 23го, теперь у нас 8.1+
1 Ещё
показать полностью
Пользователь 17890  -> Всем
16 Апрель, 2021 15:38
когда до обновления ядра битрикс сайт работал без ошибок
Сталкивались ли вы с тем, что клиенты используют обновления битрикс как манипулятивный рычаг давления, чтобы спихнуть ошибки предыдущих разработчиков на тех, кто произвел обновление ядра (ядро не модифицировалось предыдущим разработчиком)?

(доводы клиента: до обновления все работало и прочее, верните все как было)

или это классическая ментальная ошибка, когда произошедшие одновременно вещи считают связанными?

или все-таки есть риски и большие при обновлении ядра?

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

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

Всегда найдется тот, кто сделает дешевле и вообще не учитывает всяких особенностей по предыдущему опыту, так как у него его просто нет. Где-то общался на fb и обсуждали тему сроков запуска интернет-магазина. Все пишут что недели 2 надо, но тут же пишут, что никогда такого не было. 2 недели типа это в идеальном сценарии, которого на их памяти не было практически никогда. Ну и встречный вопрос им - зачем тогда вы вообще озвучиваете сроки в 2 недели если это неправда?
1 Ещё
Чебан Валерий
Белокрылов Виталий, мы с вами полностью совпадаем в понимании и рисков и ожиданий клиентов, а эту тему я тут поднял, чтобы кидать на нее ссылку и, возможно, сэкономить собственные силы и время.
вам спасибо за развернутые ответы!
0 Ещё
Вайти Виталий
Чебан Валерий, статья не поможет. У кого нет опыта - им предстоит его только набраться, у кого опыт есть - всё понимают уже.
0 Ещё
показать полностью
Пользователь 17890  -> Всем
1 Декабрь, 2020 6:24
Странно, что изображение пользователя нельзя вывести в админку
вот решение, как добавить. но все же странно, что не все поля пользователей можно вывести в админку

$eventManager = \Bitrix\Main\EventManager::getInstance();
// draw user picture after login
$eventManager->addEventHandler(
   "main",
   "OnAdminListDisplay",
   /** @param CAdminUiList $this_al */
   static function (&$this_al) {
      if ($this_al->table_id == "tbl_user") {
         foreach ($this_al->aRows as &$row) {
            $userId = (int)$row->arRes['ID'];
            $picPath = CFile::GetPath( (CUser::GetByID($userId)->Fetch())["PERSONAL_PHOTO"] );
            if (trim($picPath) != '') {
               $row->aFields["LOGIN"]["view"]["value"] .= ' <br><a target="_blank" href="' . $picPath . '">'
                  . '<img style="max-width:200px;" src="' . $picPath  . '"></a>';
            }
         }
      }
   }
);
показать полностью
Пользователь 17890  -> Всем
20 Октябрь, 2020 12:57
Опции храниться теперь и в таблице b_option_site помимо b_option
см. как подхватывает через COption::GetOptionString загруженные ранее настройки либо конкретного сайта, либо общие https://bxapi.ru/src/?module_id=main&n...tion::load

т.е. править на всякий нужно теперь в двух таблицах.

Фото:
показать полностью
Пользователь 17890  -> Всем
2 Август, 2019 17:59
Разработчикам: в CIBlockElement::GetList добавлена сортировка по переданному набору ID элементов. Этот же набор элементов необходимо передавать в фильтр метода
В информационных блоках v18.6.700 (beta) появилась возможность сортировать выборку в порядке ID, указанных в массиве:

\Bitrix\Main\Loader::includeModule('iblock');

// сортировать в таком порядке ID
$ids = [115, 120, 117, 109, 128];
$rs = \CIBlockElement::GetList(
   ['ID' => $ids],
   ['IBLOCK_ID' => '5', 'ID' => $ids],
   false, false,
   ['ID', 'IBLOCK_ID', 'NAME']
);
while ($ar = $rs->Fetch()) {
   echo $ar['ID'] . ' ';
}

// имеем вывод:
// 115 120 117 109 128


все работает.

раньше такую задачу приходилось решать через sql:

SEL ECT `ID`, `IBLOCK_ID`, `NAME` FR OM `b_iblock_element` 
WHERE `ID` IN (115, 120, 117, 109, 128) AND `IBLOCK_ID` = 5
ORDER BY FIELD(`ID`, 115, 120, 117, 109, 128);
Теги:
Дерманов Марк
Уже лет 5 ждем?
Не думаю, что дождемся xD
0 Ещё
Данилов Василий
а в CIBlockSection::GetList ?  
0 Ещё
Жуков Евгений
Данилов Василий, не планировалось
1 Ещё
показать полностью
Пользователь 17890  -> Всем
18 Апрель, 2016 3:20
PHP 5.6 on Bitrix Vs. PHP7 on Bitrix
Успешно запустил свой домашний блог, написанный на bitrix, на версии php7.    
Версия bitrix – последняя beta.    
Замерил утилитой ab-тестирования (из комплекта с Апатчем) скорость работы в первом и втором случаях:  
 
Методика тестирования:  
Меняем версию PHP в настройках апатча и прогоняем тесты ab-утилитой.  
Также смотрим на время генерации страницы модулем производительности.  
Ничего особенно, но для ответа на вопрос:  

а быстрее ли bitrix с php7?  

и примерно на сколько быстрее?  

- ответ можно получить.  
 
 Команда на тестирование урла сервера:    
d:\server\Apache\bin>ab -n 5000 -c 10 -s 300 -k http://***********:80**/w***h/m*****************i/
 Справка по параметрам ab-утилиты:  
Usage: ab [options] [http://]hostname[:port]/path
Options are:
    -n requests     Number of requests to perform
    -c concurrency Number of multiple requests to make at a time
    -s timeout      Seconds to max. wait for each response
                    Default is 30 seconds
    -k              Use HTTP KeepAlive feature
…  
Результаты следующие:
  PHP 5.6 on Bitrix        Vs.     PHP7 (Zend Engine v3.0.0) on Bitrix  
  1.png       2.png    
 
Apache/2.4.20 (Win64) PHP/5.6.20
Document Length:        2987 bytes
Concurrency Level:      10
Time taken for tests:   134.852 seconds
Complete requests:      5000
Failed requests:        2
Requests per second:    37.08 [#/sec] 
Time per request:       269.703 [ms] 
Time per request:       26.970 [ms] (mean, across all concurrent requests)
Transfer rate:          130.68 [Kbytes/sec] received

 
Apache/2.4.20 (Win64) PHP/7.0.5
Document Length:        2987 bytes
Concurrency Level:      10
Time taken for tests:   130.592 seconds
Complete requests:      5000
Failed requests:        2
Requests per second:    38.29 [#/sec] 
Time per request:       261.183 [ms] 
Time per request:       26.118 [ms] (mean, across all concurrent requests)
Transfer rate:          133.86 [Kbytes/sec] received

 
Percentage of the requests served within a certain time (ms)
  50%     98
  66%    104
  75%    108
  80%    111
  90%    120
  95%    131
  98%    151
  99%    215
 100%  42081 (longest request)

         
Percentage of the requests served within a certain time (ms)
  50%     77
  66%     83
  75%     88
  80%     91
  90%    105
  95%    123
  98%    148
  99%    187
 100%  42048 (longest request)

  3.png      4.png    
 
Как видно, местами процентов на 40% быстрее. Что интересно, что и скорость отдачи контента сервером увеличилась (см. transfer rate). Как мне кажется, это хороший показатель.  
 
PS
\2 в момент запуска php7 столкнулся со следующей ошибкой компонента:    
// \bitrix\components\bitrix\blog.blog\component.php
   global ${$arParams["FILTER_NAME"]};
   $arFilter = $$arParams["FILTER_NAME"]; // ошибка php7:
   if(!is_array($arFilter))
      $arFilter = array();

[ParseError] 
syntax error, unexpected '[', expecting ',' or ';' (0)
Это по причине того, что php7 более верно теперь расшифровывает переменные-переменных (см Changes to the handling of indirect variables, properties, and methods).  
 
Лечится более четким указанием определяемого значения переменной через {}:  
// \bitrix\components\bitrix\blog.blog\component.php   
   global ${$arParams["FILTER_NAME"]};
   $arFilter = ${$arParams["FILTER_NAME"]};  // вот так мы явно указываем имя переменной
   if(!is_array($arFilter))
      $arFilter = array();
 
PS Выходит очень круто, что просто обновляя версию php, получаем прирост! Очень радует, что битрикс понимает важность перехода на php7, и что уже даже что-то работает ;)  
 
Очень надеюсь, что скоро все будем писать на php7.  
Лазаров Адриан
проблема решина, в .htaccess  - <IfModule mod_php5.c>  перевел на <IfModule mod_php7.c>
2 Ещё
Кирсикова Евгения
вдруг кому пригодится, для поиска $$ в файлах через ssh

find /needle_folder/ -name "*.php" -exec grep '\$\$' -Hn {} \;  
3 Ещё
Микулич Евгений
https://www.youtube.com/watch?v=wCZ5TJCBWMg

Тут как бы создатель PHP  говорит, что прирост должен быть на 5-10%, если больше, то ваш проект это куча дурнопахнущего кода
0 Ещё
показать полностью
Пользователь 17890  -> Всем
23 Июль, 2017 4:32
Еще один способ бороться с DDoS-атаками: модуль для апатча mod_evasive2
DDoSSS!!! SOS!!!

CoqkE.jpg

Есть модуль, который позволяет автоотлавливать дос-ботов, или наглых парсеров, одним словом бороться с DDoS.

Конфигурируем Апатч, включаем модуль,

1/ редактируем файл httpd.conf:

LoadModule evasive2_module modules/mod_evasive2.so

<IfModule evasive2_module>
    DOSDisplayToken Off
    DOSHashTableSize 3097
    DOSPageCount 2
    DOSSiteCount 50
    DOSPageInterval 1
    DOSSiteInterval 1
    DOSBlockingPeriod 10
    DOSLogDir "<Your-dir>Apache/logs"
    DOSWhitelist 127.0.0.1
    DOSWhitelist 192.168.1.*
</IfModule>


В итоге поганцы отлавливаются и блокируются:

2017-07-23_04-05-47.png

2/
Имеем еще одну защиту нашего апатча от DDoS-атак.

Интересно было бы, если кто-то еще поделился своим опытом использования модуля,
в принципе, при наличии прямых рук легко прикручивается к апатчу на bitrix VM.

Либо может еще есть удобные какие-то способы у вас.

[для врезки вправо]

На последок для выявления перегруза слотов апатча, команды через SSH:

4/
вывести список самых активных посетителей
cat /var/log/httpd/access_log | awk ' {print $1}' | sort | uniq -c | sort -n | tail -n 10


5/
Инфо по посетителю 5.255.253.63:
cat /var/log/httpd/access_log | grep 5.255.253.63 | head -1
Гусев Александр
ddos в идеале отлавливать до хостера... в случае похуже – на хостере... в еще более худшем случае – до nginx/apache...
Но отлавливать на апаче – это, извините, стыд... Хотя, если атака слабенькая, то пост имеет место быть.
1 Ещё
Чебан Валерий
Гусев Александр, цель статьи была просветительская, что есть еще и такая возможность.
1 Ещё
Чебан Валерий
yum install mod_evasive -y
0 Ещё

Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».