• Как распарсить JSON ответ VK.api?

    Наболее удобно будет парсить вот таким способом.
    spoiler
    package main
    
    import (
    	"encoding/json"
    	"log"
    )
    
    type RawResponse struct {
    	Response []json.RawMessage `json:"response"`
    }
    
    type Response struct {
    	Id          int          `json:"id"`
    	FromId      int          `json:"from_id"`
    	ToId        int          `json:"to_id"`
    	Date        int          `json:"date"`
    	PostType    string       `json:"post_type"`
    	Text        string       `json:"text"`
    	SignerId    int          `json:"signer_id"`
    	IsPinned    int8         `json:"is_pinned"`
    	Attachment  Attachment   `json:"attachment"`
    	Attachments []Attachment `json:"attachments"`
    }
    
    type Attachment struct {
    	Type  string `json:"type"`
    	Photo *Photo `json:"photo"`
    	Link  *Link  `json:"link"`
    }
    
    type Photo struct {
    	Pid        int    `json:"pid"`
    	Aid        int    `json:"aid"`
    	OwnerId    int    `json:"owner_id"`
    	UserId     int    `json:"user_id"`
    	Src        string `json:"src"`
    	SrcBig     string `json:"src_big"`
    	SrcSmall   string `json:"src_small"`
    	SrcXbig    string `json:"src_xbig"`
    	SrcXxbig   string `json:"src_xxbig"`
    	Width      int    `json:"width"`
    	Height     int    `json:"height"`
    	Text       string `json:"text"`
    	Created    int    `json:"created"`
    	Access_key string `json:"access_key"`
    }
    
    type Link struct {
    	Url         string `json:"url"`
    	Title       string `json:"title"`
    	Description string `json:"description"`
    	ImageSrc    string `json:"image_src"`
    	ImageBig    string `json:"image_big"`
    }
    
    func main() {
    	raw := []byte(`{"response":[2468,{"id":5513,"from_id":-58014516,"to_id":-58014516,"date":1453516206,"post_type":"post","text":"Мир вам и ближним вашим!<br><br>Оправдываться - грех. Прп. авва Исайя поучал: \"Венец добродетелей - любовь; венец страстей - оправдание грехов своих\". Вместо оправданий будем говорить - \"прости\".<br><br>Братия Троицкой Селенгинской обители","signer_id":235194773,"is_pinned":1,"attachment":{"type":"photo","photo":{"pid":404037784,"aid":-7,"owner_id":-58014516,"user_id":100,"src":"http:\/\/cs631329.vk.me\/v631329773\/eefe\/8qL1uNcH-Kg.jpg","src_big":"http:\/\/cs631329.vk.me\/v631329773\/eeff\/IvmP9O8aFLc.jpg","src_small":"http:\/\/cs631329.vk.me\/v631329773\/eefd\/B6ADu5ntll0.jpg","src_xbig":"http:\/\/cs631329.vk.me\/v631329773\/ef00\/bfMgaBRpBcI.jpg","src_xxbig":"http:\/\/cs631329.vk.me\/v631329773\/ef01\/qcrVhaq--Ic.jpg","width":1000,"height":666,"text":"","created":1453470652,"access_key":"053e6253631cae5038"}},"attachments":[{"type":"photo","photo":{"pid":404037784,"aid":-7,"owner_id":-58014516,"user_id":100,"src":"http:\/\/cs631329.vk.me\/v631329773\/eefe\/8qL1uNcH-Kg.jpg","src_big":"http:\/\/cs631329.vk.me\/v631329773\/eeff\/IvmP9O8aFLc.jpg","src_small":"http:\/\/cs631329.vk.me\/v631329773\/eefd\/B6ADu5ntll0.jpg","src_xbig":"http:\/\/cs631329.vk.me\/v631329773\/ef00\/bfMgaBRpBcI.jpg","src_xxbig":"http:\/\/cs631329.vk.me\/v631329773\/ef01\/qcrVhaq--Ic.jpg","width":1000,"height":666,"text":"","created":1453470652,"access_key":"053e6253631cae5038"}},{"type":"link","link":{"url":"http:\/\/selenginskii-monastery.cerkov.ru\/sms-rassylka-monastyrya-pouchenie-dnya\/pouchenie-dnya-373\/","title":" » Поучение дня","description":"","image_src":"http:\/\/cs631418.vk.me\/v631418773\/c773\/OMWEVcI7cno.jpg","image_big":"http:\/\/cs631418.vk.me\/v631418773\/c775\/PbCrn5A8iuA.jpg"}}],"comments":{"count":0},"likes":{"count":77},"reposts":{"count":9}}]}`)
    
    	var raw_res RawResponse
    	err := json.Unmarshal(raw, &raw_res)
    	if err != nil {
    		log.Fatal("Error parsing json: ", err)
    	}
    
    	var res Response
    	err = json.Unmarshal(raw_res.Response[1], &res)
    	if err != nil {
    		log.Fatal("Error parsing json: ", err)
    	}
    
    	log.Printf("%+v", res)
    	log.Println(res.Attachments[0].Photo.Pid)
    	log.Println(res.Attachments[1].Link.Title)
    }


    Решаем проблему первого массива с помощью json.RawMessage, а проблему attachments с помощью указателей на структуры. Если в JSON поле будет отсутствовать, указатель будет равен nil.
    Ответ написан
    2 комментария
  • Какие цели и задачи у Docker в production?

    dimonchik2013
    @dimonchik2013
    non progredi est regredi
    заменяем гипервизор: виртуализация дешевле любого, в т.ч. того же Vagrantа
    + расшариваемый доступ на чтение
    + секурность какая-никакая, хоть это отдельная тема

    идея проста: процессор, как правило, дешевле памяти, т.к. редко нагружен более 50%, поэтому для кучи проектов дешевле держать один сервер с много памяти, чем несколько серверов (хороший канал, разумеется)

    у нас устроено так: nginx на хосте, он же занимается статикой, статика тоже на хосте, а upstream-ы с динамикой (Flask, Django, по-моему bottlepy еще есть) - все в контейнерах, sphinx в контейнере, еще один nginx c сишным демоном тоже в контейнере

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

    тут особых наворотов нет - только уверенность в работоспособности других сайтов, если какой-то сложит программер, но вариации могут быть самыми разными - например, nginx с самописными/специфическими модулями в контейнере не ложит остальное, где-то применить Varnish для кеширования, потестить какой-нибудь Aerospike без боязни положить систему и т.п.
    Ответ написан
    3 комментария
  • Какую версию Python изучать в конце 2015 года?

    @Tabletko
    никого не трогаю, починяю примус
    Python 3
    Ответ написан
    Комментировать
  • Кто поможет найти руководство по Haskell?

    leventov
    @leventov
    Ответ написан
    Комментировать
  • Можно ли и нужно ли передавать JSON-объект от PHP в ANGULAR в кастомном HTTP заголовке?

    miraage
    @miraage
    Старый прогер
    Easy.
    <!-- тег должен лежать до загрузки скриптов приложения -->
    <script>
    window.userInfo = <?= json_encode(Session::get('user')); ?>;
    </script>
    
    <!-- внутри приложения -->
    <script>
    app.config(function($provide) {
      $provide.constant('userInfo', angular.copy(window.userInfo));
    });
    </script>
    Ответ написан
    3 комментария
  • Какой выбрать язык для серверной части highload проекта?

    miraage
    @miraage
    Старый прогер
    Я вас умоляю. Люди на симфони обрабатывают миллиард запросов в неделю.
    Грамотно спроектируйте логику, базу данных, кэширование, балансировку - всё будет летать.
    И технология не сильно влияет. Главное, это Ваши навыки.
    Ответ написан
    Комментировать
  • Какой выбрать язык для серверной части highload проекта?

    index0h
    @index0h
    PHP, Golang. https://github.com/index0h
    С нодой очень вероятно - хлебнете горя на утечках памяти. Нода - конечно не плохая штука, но вы должны понимать, что ограничены N потоков запросов к БД, где N - количество CPU(в случае, если модуль кластер использовать не будете N = 1). То, что касается IO - нода очень даже синхронная.
    На счет C++ - будьте осторожны, скорость разработки и поддержка вероятно будут больным вопросом.
    На счет питона ничего не скажу.
    Можете посмотреть в сторону Golang или Java.

    Вообще говоря - нельзя подбирать технологию по паре абстракций типа "работа с postgre", "логика проекта", "highload")) Это как подбирать пароль зная, что но был написан на желтой бумажке. Слишком мало информации.

    На счет HL: возможно скажу спорную штуку, но если вы ориентируетесь на HL - даже не предполагайте того, что сервер будет один.
    Ответ написан
  • Какой выбрать язык для серверной части highload проекта?

    xmoonlight
    @xmoonlight
    https://sitecoder.blogspot.com
    php - все будет в лучшем виде, если руки "прямые".
    Ответ написан
    2 комментария
  • Есть ли эффективная реализация словаря у которого ключ регулярное выражение?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D...

    Ну и да, быстрее O(N) вы не сделаете. Да и за O(N) этого не добиться... вам же еще подстроку искать.
    Ответ написан
    Комментировать
  • Какие известные проекты написаны на Angular?

    SowingSadness
    @SowingSadness
    web-разработчик
    Нет таких. Маркетинговый булщит.
    Ответ написан
    Комментировать
  • Дети и интернет: как контролировать?

    В самую первую очередь стоит показать детям Двач. А потом уже можете об этом вопросе совершенно не беспокоиться.
    Ответ написан
    1 комментарий
  • На каких языках лучше написать сайт для турфирмы (нужно чтобы было фильтры поиска, чтобы использовалась статистика и аналитика)?

    vvpoloskin
    @vvpoloskin
    Инженер связи
    Какой цвет подойдет для покраски забора - красный, синий или зеленый? Два последних я не видел.
    Ответ написан
    Комментировать
  • Создать демона на python 2.7 ?

    @bromzh
    Drugs-driven development
    Всегда использую supervisord. Просто отличная штука, ещё и на питоне написанная. Гибкие конфиги, перезапуск при падении. и другие плюшки.

    UPD
    Как использовать:
    1) Ставишь его общесистемно sudo apt-get install supervisor (можно и через пип поставить, но настраивать надо побольше будет).
    2) Надо настроить сам супервизор: откуда он будет брать конфиги, какие права нужны и т.д. Если у тебя убунта, то настройки самого супервизора будут в порядке. Они хранятся в /etc/supervisor/supervisord.conf. В этом файле прописано, что настройки для демонов будут читаться из папки /etc/supervisor/conf.d
    Если у тебя не убунта, или ты ставил супервизор через пип, то вот стандартный конфиг. Настройки там очевидные
    3) Когда супервизор поставлен и настроен, запускай его:
    sudo service supervisor start
    или
    sudo supervisord

    4) Теперь надо сделать конфиг для запуска твоего скрипта. Создаёшь файл в папке с конфигами такого содержания:
    [program:ОТОБРАЖАЕМОЕ_ИМЯ]
    command = КОМАНДА_ДЛЯ_ЗАПУСКА (например python ИМЯ_СКРИПТА)
    autorestart = true # автостарт при запуске супервизора
    stderr_logfile = /var/log/ИМЯ_ЛОГФАЙЛА.err.log # тут перенаправляем stderr в файл
    stdout_logfile = /dev/null # тут глушим весь вывод со stdout


    Всё! Управлять твоими демонами можно через команду supervisorctl. Сперва надо перепрочитать конфиг, потом стартануть приложение:
    supervisorctl reread
    supervisorctl ОТОБРАЖАЕМОЕ_ИМЯ start

    Можно опустить ОТОБРАЖАЕМОЕ_ИМЯ, тогда команда будет применена ко всем.
    Также ты можешь запустить CLI, если введёшь supervisorctl без параметров. Там по табу смотри команды.
    Читай этот раздел, для понимания конфигов запускаемых приложений.
    Ответ написан
    1 комментарий
  • Правильно ли сформирован запрос на php, mysql?

    nowm
    @nowm
    1. Точка с запятой необязательна, если выполняется только одна инструкция. В вашем случае её можно не писать, так как выполняется только одна инструкция «INSERT». Если инструкций несколько, то каждая инструкция обязательно должна заканчиваться точкой с запятой (кроме последней инструкции, кажется — там не обязательно).

    2. Да, запрос правильный.

    Тот запрос, который у вас был «как вариант», не будет работать, потому что вы пытаетесь нормальный PHP-шный NULL скопировать в строку. Там не появится слово «NULL» в итоге, так как вместо него подставится пустая строка. Итоговый запрос будет выглядеть примерно так:

    INSERT INTO users (user_id, user_name, user_adddate) VALUES ('', 'user', '1404721764')


    У вас здесь сразу две ошибки. Первая — вы передаёте значение NULL, а нужно передавать строку со словом «NULL». Вторая — вы пытаетесь сгененрировать строку так, что у вас слово «NULL» будет в SQL-запросе стоять в кавычках. В таком случае это для SQL-сервера будет считаться не значением «NULL», а строкой. Будет выдана ошибка, потому что у вас user_id — целочисленное, а вы пытаетесь в него писать строку. Правильнее будет так (разобью на строки для читаемости):

    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (%s, '%s', %d)
    ", 'NULL', $user_name, time());
    ?>


    Со sprintf вы движетесь в правильном направлении. После этого вам будет легко понять принцип работы подготовленных запросов PDO, которые я вам советую использовать вместо mysql_*-функций.

    <?php
    /* 
     * Инициализация. Эту строку нужно писать там, 
     * где вы делаете mysql_connect. А сам вызов mysql_connect 
     * и прочего сопутствующего можно удалить.
     */
    $db = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'dbuser', 'dbpass');
    
    // . . .
    
    /* 
     * Вставляемые переменные заменяются знаками вопроса. Никаких кавычек
     * тут ставить не нужно — PDO сам поставит их в нужном месте.
     */
    $sth = $db->prepare('INSERT INTO `users` (user_id, user_name, user_adddate) VALUES (NULL, ?, ?)');
    
    /* 
     * Передача значений в запрос происходит в виде массива;
     * порядок элементов в массиве имеет значение. 
     * $user_name — первый элемент, значит его значение подставится 
     * на месте первого знака вопроса. time() — второй элемент, значит
     * его результат подставится вместо второго «?»
     */
    
    $sth->execute( array($user_name, time()) );
    ?>


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

    <?php 
    // . . . 
    
    $sth = $db->prepare('INSERT INTO `users` (user_id, user_name, user_adddate) VALUES (?, ?, ?)');
    
    /*
     * Важный момент: нельзя писать
     * $sth->bindParam(1, NULL, PDO::PARAM_NULL);
     * Перед тем, как биндить NULL, его нужно присвоить переменной 
     * и биндить уже эту переменную. Иначе будут выскакивать ошибки.
     */
    $null_value = NULL;
    $sth->bindParam(1, $null_value, PDO::PARAM_NULL);
    
    $sth->bindParam(2, $user_name, PDO::PARAM_STR);
    $sth->bindParam(3, time(), PDO::PARAM_INT);
    
    /*
     * Так как параметры уже переданы с помощью bindParam-ов,
     * в вызов execute не нужно ничего добавлять.
     */
    $sth->execute();
    ?>


    По поводу NOW() или CURDATE(). У вас выскакивает ошибка потому, что это не функции PHP. Это функции MySQL и их нужно писать в запросе как есть — они выполняются не в интерпретаторе PHP.

    Как я понял, вы пытались что-то вроде этого делать:

    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (%s, '%s', %d)
    ", 'NULL', $user_name, NOW());
    ?>


    А нужно так:
    <?php
    $insert_sql = sprintf("
        INSERT INTO users (user_id, user_name, user_adddate) 
        VALUES (NULL, '%s', NOW())
    ", $user_name);
    ?>


    Так же, если вы добавляете user_adddate с помощью NOW() или CURDATE(), лучше для этого столбца изменить тип с bigint(20) на datetime.
    Ответ написан
  • Есть ли новая литература по PHP?

    Смотрим на рутрекере в разделе литературы по программированию. Используйте фильтр по ЯП и году.
    Самые лучшие книги и новинки появляются именно там
    Ответ написан
    Комментировать
  • Какой фреймворк выбрать?

    @mandrozz
    Я бы посоветовал посмотреть в сторону Laravel
    Ответ написан
    Комментировать
  • По каким параметрам надо выбирать vps-хостинг для сайта?

    @inkvizitor68sl
    Linux-сисадмин с 8 летним стажем.
    vps-хостинг определяется не по "количеству посетителей" и прочей чуши, а по количеству выделенной памяти, процессора и диска (ssd/hdd) вашей виртуальной машине. У OpenVZ есть ещё куча невнятных лимитов, поэтому его покупать не стоит.
    А дальше всё зависит от вашего приложения, насколько оно оптимизировано, насколько потюнены демоны - в зависимости от этого и получится "количество запросов в секунду".
    Ответ написан
    4 комментария
  • JS-деформация изображения из точки к курсору?

    @rowdyro
    #canvas #scale #rotate #fabricjs
    Ответ написан
    Комментировать
  • Каковы основные принципы регистрации и авторизации через социальные сети OAuth2?

    hbuser
    @hbuser Автор вопроса
    Отвечу сам себе.
    Здесь есть полезная конкретная информация о технической реализации.

    А если вкратце, то...

    Для авторизации, регистрации используется все та же таблица 'users'. Вместе с обычной регистрацией и авторизацией, когда при регистрации (в самом простом виде) в таблицу 'users' добавляются email, password и login пользователя, а при авторизации проверяется соответствие введенных login'а и password'а существующим в базе данных, аналогичным образом используется и регистрация/авторизация через социальные сети. Только в данном случае источником данных о пользователе для его регистрации является не непосредственный пользователь, который вводит данные в форму, а соц. сеть. Регистрация в данном случае достаточно прозрачная, т.е. не видна пользователю. Схема примерно следующая (без особенностей работы Oauth-протокола):


    1) Пользователь выбирает вход через соц. сеть.
    2) Происходит обращение к странице авторизации в этой соц. сети, если человек еще не авторизовывался там. После ввода данных, а если он ранее авторизовывался, происходит запрос на разрешение использования его данных.
    3) Если человек отказывается, то на этом конец. Если дает согласие, то выполняется перенаправление на указанную в настройках Oauth страницу сайта.
    4) У каждого пользователя в соц. сетях есть свой уникальный идентификатор, который можно запрашивать. Для своей таблицы 'users' нужно добавить пару дополнительных полей (например, вот такие): auth_via (enum('native, 'vk', 'mailru', '...')) - для обозначения типа регистрации пользователя, и social_id - здесь будет храниться уникальный идентификатор в соц. сети. Если нужно хранить какие-то специфические данные этого пользователя из соц. сетей, то можно создать доп. поля для этих данных.
    5) После того, как пользователь дал разрешение на использование его данных, необходимо запросить нужные данные от соц. сети, в т.ч. и идентификатор пользователя в соц. сети. Вот здесь и начинается невидимый процесс регистрации. Нужно проверить есть ли в БД пользователь с таким social_id, если нет, то вставляем social_id, данные пользователя из соц. сети, по необходимости, в БД. Все, пользователь зарегистрирован.
    Если же данные о пользователе есть, то необходимо запросить актуальные данные из соц. сети, сравнить их с теми, что в базе и если они изменились, то обновить их и в своей базе данных, если нет, то просто переходим к следующему шагу.
    6) Создается сессия с данными пользователя.

    Таким образом, к существующей таблице "родной" регистрации пользователей сайта присоединяется, условно говоря, таблица, поля, необходимые для регистрации/авторизации через соц. сети., и друг-другу они не мешают.

    ca4a4b263fd1424085988c9deaeb6d5b.png

    Для пользователя, зарегистрированного из соц. сети пароля и логина, естественно, нет. Они нужны для авторизации. А т.к. пользователь авторизуется с помощью своих логина и пароля в соц. сети, то и указывать здесь нечего. И еще, можно при авторизации, к запросу проверки логина и пароля, добавить условие

    'AND WHERE `auth_via`="native"'

    , чтобы исключить пользователей, зарегистрированных из соц. сетей.

    Как видно, для каждого пользователя в таблице создается внутренний (внутрисайтовый, если так можно выразиться) первичный, автоинкрементный ключ. Соответственно, нет разницы для логики сайта между пользователем, зарегистрированным через соц. сеть и через сайт. Если говорить об интернет-магазине, то, для привязки заказов к пользователю, можно использовать единый, внутренний идентификатор ID.
    Ответ написан
    3 комментария