Задать вопрос
  • Разве Composer бесполезен в 2017?

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

    1. Композер реально бесполезен в некоторых проектах. Нужно смотреть от конкретной ситуации и целей. Приведу несколько примеров для понимания:

    • Проект полон легаси-кода. Ну как в вашем случае. 5.4, серьёзно? Вероятно этот проект просто на "дожитии", его надо поддерживать в состоянии "работает и ладно", а параллельно вероятно планируется его полный ремейк на нормальных технологиях.
    • CMS-ки. Ну тот же Битрикс. Сейчас еще суют туда композер, когда не знают толком нативные для Битрикса либы и его возможности из коробки. Но в 2017-м прямо 100% не стоит лезть в него с композером.
    • Проект банально очень простой. Ну нет необходимости подключать либы. Делаем допустим обычный CRUD сервис. На кой хрен в него дирижерами тыкать?
    • Делаем микросервисы. Близко к прошлому пункту. Большинство микросервисов реализуют всего лишь пару простейших операций, и их цель - весить мало, работать быстро. Грузить их каким-то мусором, пусть даже легковесным - совсем не нужно.
    • Упоротость безопасности. Бывают компании, которые блокируют всё - ресурсы, либы, технологии. Вот допустим мы в такой компании, которая запретила 90% рнр-шных либ. На кой вам композер?


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

    Ваш вопрос мне напомнил собеседование в 2020-м кажется году.
    Я тогда собеседовался на позицию CTO к одному небольшому производителю мебели. У них была сайт-витрина (ну знаете, таких тысячи - товары есть, а купить там их нельзя. По сути визитка для каждого товара, с минимумом опций). И они начали его делать на микросервисах. Ну прямо подняли кубернетес и начали его делать.
    Это первое что меня напрягло, так как очевидно, что для подобного сайта микросервисы - не нужны абсолютно. Ставишь вордпресс/битрих/тильду - и потребности бизнеса решены за бесплатно и 0 времени.
    Второе что меня окончательно отпугнуло от той технической команды - они делали микросервисы на Ubuntu + Laravel. Это прямо ад. Какой Alpine, какой Slim/Lumen, зачем это всё :) И опять же, обосновали это тем, что:
    Убунту - самая популярная линуксовая система
    Ларавель - самый популярный фреймворк
    Микросервисы - самый прорывной паттерн

    И вроде бы всё верно. Но:
    1. Мозайка не складывается
    2. Им это всё нахрен не нужно. Ни убунту, ни ларавель, ни микросервисы, ни куча разрабов. Потому что задача такая - сделать "сайт-визитку".

    При этом микросервисы я обожаю, композер юзаю, тесты пишем :)
    Но в нужном месте, в нужное время.
    Ответ написан
    Комментировать
  • Что и как тестировать в Битрикс?

    vekov
    @vekov Автор вопроса
    Спустя несколько лет отвечу на свой же вопрос :)

    1. Есть прекрасная утилита под названием Codeception. Она впихивается в пайплайн сборки и делает свою работу, более подробно в следующих пунктах.
    2. Юнит-тесты, как писал в вопросе - при написании модулей, api, компонентов и тд - отчасти применимо.
    3. Самое вкусное - функциональные тесты. Используем простейший сценарный код, чтобы проверять, что не поломали функциональность системы. Ну то есть условно, делаем формочку, навешиваем на неё сценарий вида:
    • Я -> захожу на страницу
    • Я -> вижу форму
    • Я -> ввожу емейл
    • Я -> нажимаю отправить
    • Я -> вижу "Успешно"
    Ответ написан
    Комментировать
  • Почему не отвечает API Bitbucket Server, выдает XSRF Error?

    vekov
    @vekov Автор вопроса
    Вообще проблема была в лишних хедерах, сам себе поставил палки в колёса. Работающий вариант будет с таким набором:

    $headers = array(
        'cache-control: max-age=0',
        'upgrade-insecure-requests: 1',
        'sec-fetch-user: ?1',
        'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'x-compress: null',
        'sec-fetch-site: none',
        'sec-fetch-mode: navigate',
        'accept-encoding: deflate, br',
        'accept-language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
        "Content-Type: application/json",
        'X-Atlassian-Token: no-check',
    );
    Ответ написан
    Комментировать
  • Регистрация кастомного протокола в хром?

    У нас такой вариант отработал:

    Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\foo]
    "URL Protocol"=""
    @="URL:foo Protocol"

    [HKEY_CLASSES_ROOT\foo\DefaultIcon]
    @="\"C:\\Windows\\explorer.exe\""

    [HKEY_CLASSES_ROOT\foo\shell]

    [HKEY_CLASSES_ROOT\foo\shell\open]

    [HKEY_CLASSES_ROOT\foo\shell\open\command]
    @="cmd /c set url=\"%1\" & call set url=%%url:foo:=%% & call start explorer file:%%url%%"
    Ответ написан
    Комментировать
  • Почему получаю ошибку от RabbitMQ?

    vekov
    @vekov Автор вопроса
    Довольно глупая история получилась, просто авторизация на этом облачном сервисе не работает похоже. Поставил на отдельном VPS rabbitmq-server и всё завелось без проблем.
    Ответ написан
    Комментировать
  • Можно ли создать полноценный интернет магазин на React + Apollo graphql?

    Полноценный интернет магазин, можно создать на чём угодно. Здесь сам тех.стэк не играет особого значения. Он лишь влияет на скорость достижения цели. Я не очень хорошо дружу с реактом, но как помню, он лишь реализует фронт, Аполло же инструмент для работы с graphQL. Исходя из этого, заметно, что не хватает бэка. То есть у вас должно быть хоть что-то, что реализует логику. Те же резолверы должны быть на чём-то написаны...
    Ответ написан
    Комментировать
  • Как я могу разделить схему GraphQL на несколько файлов?

    Може быть будет полезно:
    https://itnext.io/a-guide-to-graphql-schema-federa...

    Это в продолжение темы про федерации графов.
    Ответ написан
    Комментировать
  • Как правильно сархитектурить graphQL в микросервисах?

    vekov
    @vekov Автор вопроса
    Иван Шумов, возможно дал дельный совет, но всё же я решил разобраться с федерациями. Нашёл готовый гейтвей nautilus, который на мой взгляд, идеально решает задачу.
    То есть по факту у нас сейчас 3 сервиса, и один общий гейтвей, который ретранслирует расширенный API, автоматически собирая его с других сервисов.
    Возможно, пока не столкнулся с проблемами, но на данный момент получил именно то, чего хотел.
    Насчёт "Не надо так делать. Graphql это только gateway. Использовать его как интерфейс к базе данных, даже через бизнес-логику это такое себе решение. " - на данный момент не могу полностью согласиться. Получается очень удобно расширять схемы и достаточно независимо (в плане разработчиков - каждый меняет свой граф, а не общий).
    Ответ написан
  • Как работать с сессиями в php?

    vekov
    @vekov Автор вопроса
    Проблема была довольно таки наглядная. Заключалась в выводе ошибок и лишней проверке.

    Использовал error_reporting(E_ERROR | E_PARSE);
    Чтобы не выводились предупреждения о том, что переменная не определена.

    А потом почитав немного еще доков по сессии, решил что проверка в общем-то лишняя. Оставил просто session_start(); Потому что она не только запускает новую сессию, но и продолжает существующую. Следовательно в доп проверке - нет смысла.
    Ответ написан
  • Почему не отправляется письмо на почту?

    Не работал с модхом, но я бы посоветовал проверить работает ли функция mail()

    <?php
    $success = mail('example@example.com', 'My Subject', $message);
    if (!$success) {
        $errorMessage = error_get_last()['message'];
    }
    ?>


    Только вместо адреса - свою почту подставь.

    Если не работает - смотри логи. Скорее всего в /var/log/maillog - это файл.
    Ответ написан
  • Как вывести элемент при наличии свойства (Битрикс)?

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

    Если речь о том, как разделить элементы с заполненным свойством от элементов с незаполненным, можно так:

    $IblockID = 1;
    $res = CIBlockElement::GetList([], ['IBLOCK_ID' => $IblockID], false, false, ['IBLOCK_ID', 'NAME', 'PROPERTY_ISEMPTY']);
    while($item = $res -> Fetch())
    {
        if($item['PROPERTY_ISEMPTY_VALUE'])
        {
            $arResult['FILLED'][] = $item;
        }
        else
        {
            $arResult['EMPTY'][] = $item;
        }
    }


    На выходе будет 2 массива - FILLED (где свойство заполнено) и EMPTY (где пусто).
    Вместо ISEMPTY - надо подставить код своего свойства.
    Ответ написан
  • Как вывести сервис Minikube во внешнюю сеть?

    vekov
    @vekov Автор вопроса
    Итак, ответ на этот вопрос заключался в следующем:

    Нужно было поставить на сервак nginx, и сделать proxy_pass на ингресс.

    Типа такого:
    server {
        listen 80;
        server_name blog.zeroxzed.ru;
        access_log /var/log/nginx/blog.zeroxzed.ru-access.log;
        error_log /var/log/nginx/blog.zeroxzed.ru-error.log;
    
    location / {
        proxy_pass http://192.168.13.31;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        }
    }


    Это мы размещаем в etc/nginx/sites-available/default

    Айпишник "192.168.13.31" конечно же меняем на ип ингресса.
    Получить его можно через:
    kubectl get ingress

    Вроде бы :)
    Ответ написан
    Комментировать
  • Что за ошибка в логах сервера?

    Кавычка закрывает строку после s

    И попробуй так:
    preg_replace("[^a-zA-Z0-9\s'\:\/\[\]\-\pL]",'', $str);
    Ответ написан
    Комментировать
  • Как подставить значение в input?

    $('#FormRow-normal-price').val('12'); - вполне работает. Попробовал только что.

    Посмотрите в консоли браузера, может какая-то ошибка на странице, которая не дает вашему скрипту запуститься? F12.
    Или может быть кеш страницы... Ctrl+F5.
    Ответ написан
    Комментировать
  • Как разместить два домена на одном хостинге?

    А есть ли ISP панель? Обычно к хостингам она прилагается. Если есть, то просто там прописать два www-домена, или два доменных имени.
    Ответ написан
    1 комментарий
  • Ошибка minukube/kubernetes: The connection to the server ***:8443 was refused - did you specify the right host or port?

    vekov
    @vekov Автор вопроса
    1) Проверить запущен ли докер? Если нет, то запустить.
    service docker status
    service docker restart

    2) Проверить запущен ли kubelet? Если нет, - запускаем.
    service kubelet status
    service kubelet restart

    3) Отключаем swap:
    swapoff -a
    Ответ написан
    Комментировать
  • Как определить долгий авиамаршрут?

    vekov
    @vekov Автор вопроса
    Вдруг кому то пригодится. В итоге красивого решения не обнаружил, похоже его и нет. Сделал следующим образом:

    $Longest_Path = new CLongestPathBuilder($routes); // создаем экземпляр класса
    $Longest_Path -> printLongestPath(); // выводим информацию о самом долгом маршруте
    
    
    
    Class CLongestPathBuilder
    {
    	protected $arResult = [];
    	protected $iterator = 0;
    
    	function __construct($routes)
    	{
    		// Sorting routes array by date (dt_start field)
    		try
    		{
    			$this->arResult['routes'] = CExtraTools::sortArrayByDate($routes);
    		}
    		catch(Exception $e)
    		{
    			echo $e->GetMessage();
    			exit(1);
    		}
    
    		foreach($this->arResult['routes'] as $route)
    		{
    			$this->arResult['path'][$this->iterator]['full_path'] .= $route['start'].' -> '.$route['finish'];
    			$this->arResult['path'][$this->iterator]['routes_count'] = 1; 
    			// Trying to find all available routes from last destination ($route['finish'])
    			$this->getRoutes($route);
    			$this->iterator++;
    		}
    	}
    
    	protected function getRoutes($route1)
    	{
    		// Get available routes from last destination
    		if(!$this->arResult['path'][$this->iterator]['prev_date'])
    		{
    			$this->arResult['path'][$this->iterator]['prev_date'] = $route1['dt_start'];
    		}
    		foreach($this->arResult['routes'] as $route2) 
    		{
    			if( ($route1['start'] != $route2['start']) && ($route1['finish'] == $route2['start']) && ($this->arResult['path'][$this->iterator]['prev_date'] < $route2['dt_start']) )
    			{
    				$this->arResult['path'][$this->iterator]['full_path'] .= ' -> '.$route2['finish'];
    				$this->arResult['path'][$this->iterator]['days'] += CExtraTools::diffDates($route1['dt_start'], $route2['dt_start']);
    				$this->arResult['path'][$this->iterator]['dates'][] = $route1['dt_start'].' - '.$route2['dt_start'];
    				$this->arResult['path'][$this->iterator]['prev_date'] = $route2['dt_start'];
    				$this->arResult['path'][$this->iterator]['routes_count']++; // Number of routes in the path
    				$this->setLongest($this->arResult['path'][$this->iterator]['days']); // Setting current path as the longest one
    				$this->getRoutes($route2);
    			}
    		}
    	}
    
    	protected function setLongest($duration)
    	{
    		// a function used to set longest path
    		if($duration > $this->arResult['longest_path']['days'])
    		{
    			$this->arResult['longest_path'] = $this->arResult['path'][$this->iterator];
    		}
    	}
    
    	public function getLongest()
    	{
    		// a function used to get longest path
    		return $this->arResult['longest_path'];
    	}
    
    	public function getPaths()
    	{
    		// a function used to get longest path
    		return $this->arResult['path'];
    	}
    
    	public function printAllPaths()
    	{
    		CExtraTools::pre($this->arResult['path']);
    	}
    
    	public function printLongestPath()
    	{
    		CExtraTools::pre($this->arResult['longest_path']);
    	}
    }
    
    
    
    Class CExtraTools
    {
    	public static function diffDates($date1, $date2)
    	{
    		// The function to count the difference between two dates
    		try
    		{
    			$datetime1 = new DateTime($date1);
    			$datetime2 = new DateTime($date2);
    		}
    		catch (Exception $e) {
    			echo $e->getMessage();
    			exit(1);
    		}
    		$interval = $datetime1->diff($datetime2);
    		return $interval->invert ? $interval->days*(-1) : $interval->days;
    	}
    
    	public static function pre($var)
    	{
    		// modification of print_r method
    		echo '<pre>';
    		print_r($var);
    		echo '</pre>';
    	}
    
    	protected static function cmp($a, $b)
    	{
    		// only to sort array in sortArrayByDate() function
    		if ($a['dt_start'] == $b['dt_start']) {
    			return 0;
    		}
    		return ($a['dt_start'] < $b['dt_start']) ? -1 : 1;
    	}
    
    	public static function sortArrayByDate($array)
    	{
    		// Sort array by date (dt_start field)
    		if(!is_array($array))
    		{
    			throw new Exception('Должен быть передан массив');
    		}
    		usort($array, "self::cmp");
    		return $array;
    	}
    }
    Ответ написан
    Комментировать
  • Почему не срабатывает git commit в shell_exec()?

    vekov
    @vekov Автор вопроса
    Для отладки стоит использовать такую схему:

    $git_branches = exec('git push origin master:'.$_GET["REMOTE"].' 2>&1', $stdin, $stderr);


    То есть shell_exec меняем на exec, и добавляем "2>&1"; Тогда сможем видеть ошибки.

    Сама команда commit не работала, потому что в конфиге был не верно задан пользователь. И потому что у пользователя было не достаточно прав. Назначил его владельцем папки .git. Проблема решилась
    Ответ написан
    Комментировать
  • Как реализован jsFiddle?

    1)
    - Валидация каждого поля
    - Генерируется страничка, куда в отдельные теги записывается код из полей, которые ты заполнил (JS - > <script></script>, CSS -> <style></style> и HTML -> <body></body>)
    - Она вставляется в iframe справа снизу.
    2) Ссылку можно генерировать как ID записи в БД, например.

    Можно вместо бд файлы конечно использовать, но оно тебе надо?
    Ответ написан
    Комментировать