воскресенье, 5 ноября 2017 г.

history

если нужно найти недавно выполненную команду в истории, но помните только часть
`history | grep console` где `console` искомое слово,
выведет нумерованный список команд, находите нужную и запускаете ее по номеру `!541`

вторник, 31 октября 2017 г.

update linux


========
sudo apt-get update && sudo apt-get upgrade && sudo apt-get clean
========

sudo apt-get update && sudo apt-get dist-upgrade -y && sudo apt-get clean


update - обновляет списки

dist-upgrade - устанавливает обновления (в том числе системные)

-y - согласие на установку

clean - чистит кеш от уже ненужных пакетов


sudo do-release-upgrade - обновить релиз

суббота, 28 октября 2017 г.

Foreign key

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

Пример

Самое время продемонстрировать все на практике. Этот пример написан для MySql, если вы используете другую СУБД, прочитайте сперва документацию по ней. Этот блок SQL содержит исходный код создания таблиц для нашего примера:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      CREATE TABLE usr (
        usr_id  INT AUTO_INCREMENT NOT NULL,
        FIRST  VARCHAR(25) NOT NULL,
           surname  VARCHAR(50) NOT NULL,
           PRIMARY KEY(usr_id)
         ) ENGINE=InnoDB CHARACTER SET=UTF8;
      CREATE TABLE product (
           prod_id  INT AUTO_INCREMENT NOT NULL,
          name  VARCHAR(40) NOT NULL,
          descr  VARCHAR(255) NOT NULL,
          PRIMARY KEY(prod_id)
        ) ENGINE=InnoDB CHARACTER SET=UTF8;

      CREATE TABLE invoice (
          inv_id  INT AUTO_INCREMENT NOT NULL,
          usr_id  INT NOT NULL,
          prod_id  INT NOT NULL,
          quantity INT NOT NULL,
          PRIMARY KEY(inv_id)
        ) ENGINE=InnoDB CHARACTER SET=UTF8;
А на это Диаграммы Отношений, для этих таблиц:
db_phase1.png
В таком виде, как это представлено сейчас, таблицы не связаны между собой, кроме как через названия колонок.
Могли бы мы хранить всю информацию в одной таблице? Конечно! Можно было бы создать таблицу, которая содержала бы в себе всю информацию о покупателе, данные о товаре и количество заказанных товаров. Но что случится, если мы допустим ошибку в одной из записей в описании товара или имени покупателя? О, нет, теперь все испорчено! Получим ли мы записи с опечатками вместе с другими записями? Что если нет? У нас некорректные данные — небеса рухнули.

Добавляем внешние ключи

Теперь пришло время установить связи между таблицами, которых не хватало до сих пор. Соединим колонки usr_id и prod_id таблицы invoice с их соответствиями в таблицах usr и product.
Вы можете выполнить команду ALTER для таблицы invoice, но я предпочитаю вносить такие изменения на этапе проектирования, поэтому можно внести изменения прямо запрос создания таблицы:
1
2
3
4
5
6
7
8
9
  CREATE TABLE invoice (
    inv_id  INT AUTO_INCREMENT NOT NULL,
    usr_id  INT NOT NULL,
    prod_id  INT NOT NULL,
    quantity INT NOT NULL,
    PRIMARY KEY(inv_id),
    FOREIGN KEY (usr_id) REFERENCES usr(usr_id),
    FOREIGN KEY (prod_id) REFERENCES product(prod_id)
  ) ENGINE=InnoDB CHARACTER SET=UTF8;
таблицы с внешними ключами
Заметьте, что в новом варианте запроса CREATE для таблицы invoice, я добавил синтаксис FOREIGN KEY () REFERENCES table(
) чтобы установить связь между таблицами.
Просто добавив объявления внешних ключей, мы добились встроенной защиты целостности данных. Если мы попытаемся выполнить запрос INSERT или UPDATE со значением внешнего ключа для таблицы invoice, база данных автоматически проверит существует ли данное значение в связанной таблице. Если указанных значений в связанных таблицах не существует — база данных не выполнит запрос INSERT/UPDATE, сохранив таким образом целостность данных.
Теперь не придется проверять вручную родительскую таблицу на существование конкретных значений, прежде чем вставить данные в таблицу-потомок. Также можете спокойно удалять записи. Хотите избежать ошибок новым способом? Меньше кодирования — лучший способ для ленивых программистов.

Поддадим газку

Готовы улучшить ваши внешние ключи еще? Да, они могу сделать значительно больше для вас.
На данный момент, у нас есть защита целостности данных на случай каких-либо манипуляций с таблицами-потомками, но что если внести изменения в родительскую таблицу? Как нам быть уверенными, что таблицы-потомки в курсе всех изменений в родительской таблице?
MySQL позволяет нам контролировать таблицы-потомки во время обновления или удаления данных в родительской таблице с помощью подвыражений: ON UPDATE и ON DELETE. MySQL поддерживает 5 действий, которые можно использовать в выражениях ON UPDATE и/или ON DELETE.
  • CASCADE: если связанная запись родительской таблицы обновлена или удалена, и мы хотим чтобы соответствующие записи в таблицах-потомках также были обновлены или удалены. Что происходит с записью в родительской таблице, тоже самое произойдет с записью в дочерних таблицах. Однако не забывайте, что здесь можно легко попасться в ловушку бесконечного цикла.
  • SET NULL:если запись в родительской таблице обновлена или удалена, а мы хоти чтобы в дочерней таблице некоторым занчениям было присвоено NULL (конечно если поле таблицы это позволяет)
  • NO ACTION: смотри RESTRICT
  • RESTRICT:если связанные записи родительской таблицы обновляются или удаляются со значениями которые уже/еще содержатся в соответствующих записях дочерней таблицы, то база данных не позволит изменять записи в родительской таблице. Обе команды NO ACTION и RESTRICT эквивалентны отсутствию
    подвыражений ON UPDATE or ON DELETE для внешних ключей.
  • SET DEFAULT:На данный момент эта команда распознается парсером, но движок InnoDB никак на нее не реагирует.
Для моей базы данных из примера, я решил, что для внешних ключей из таблицы invoice, UPDATE будут выполняться каскадно для дочерних таблиц, а удаление будет запрещено. Таким образом, любые изменения в таблицах usr и product автоматически отразятся в таблице invoice, но если товар заказан или если у пользователя есть счет — они не могут быть удалены.
Ниже представлен новый вариант запроса CREATE для таблицы invoice с внешними ключами и выражениями ON UPDATE и ON DELETE
1
2
3
4
5
6
7
8
9
10
11
12
13
      CREATE TABLE invoice (
        inv_id  INT AUTO_INCREMENT NOT NULL,
        usr_id  INT NOT NULL,
        prod_id  INT NOT NULL,
        quantity INT NOT NULL,
        PRIMARY KEY(inv_id),
        FOREIGN KEY (usr_id) REFERENCES usr(usr_id)
          ON UPDATE CASCADE
          ON DELETE RESTRICT,
        FOREIGN KEY (prod_id) REFERENCES product(prod_id)
          ON UPDATE CASCADE
          ON DELETE RESTRICT
      ) ENGINE=InnoDB CHARACTER SET=UTF8;

Выводы

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


пятница, 27 октября 2017 г.

parser

Авторизация на сайте при помощи библиотеки cUrl Продолжая цикл статей посвященный парсерам и всем, что с ними связано. В этой статье расскажу про то, как легко можно авторизоваться на любом сайте при помощи библиотеку cUrl php. Для примера я взял один Украинский портал, к которому я собственно и буду подбирать ключики. Для работы нам также понадобится библиотека simple_html_dom 
Авторизацию будем проходить на сайте http://tender.me.gov.ua , не буду создавать лишних ссылок, чтобы не накрыть нашу лавочку. 
Для начала зарегистрируйтесь на сайте, чтобы иметь тестовые логин-пароль для входа на сайт. 
На сайте мы видим ничем не примечательную форму авторизации.
Форма авторизации на сайте
"Внутренности" у нее тоже ничем не примечательны
<form id="login" name="login" method="post" action="/EDZFrontOffice/menu/uk/;jsessionid=c4651e96f5c2b9afb08776cbd1a5" enctype="application/x-www-form-urlencoded">
 <input type="hidden" name="login" value="login" />
 <input id="login:login" type="text" name="login:login" class="login" maxlength="80" />
 <input id="login:password" type="password" name="login:password" value="" maxlength="80" class="login" />
 <input type="image" src="images/t_login_button.png;jsessionid=c4651e96f5c2b9afb08776cbd1a5" name="login:j_id_id254" alt="&#1042;&#1093;&#1110;&#1076;" />
 <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id1:j_id2" autocomplete="off" />
</form>
Итак смотрим, что форма авторизации отправляет при аутентификации. Для этого возпользуемся  Google Chrome, открываем в нем сайт, затем Инструменты->Инструменты разработчика. Далее переходим на вкладку Network. Для браузера Opera Меню->Страница->Средства Разработки->Open Opera DragOnFly и вкладка Сеть. Хоть я и фанат Оперы, но на мой взгляд продукт от компании Google немного удобнее. Я конечно не про браузер =)
Теперь авторизуемся на сайте, смотрим, что посылает форма при сабмите. 
Просмотр отправляемых данных в Google Chrome
Видим, что форма посылает следующие параметры
1
2
3
4
5
6
login:login
login:login:{loginmail}
login:password:{password}
login:j_id_id254.x:8
login:j_id_id254.y:7
javax.faces.ViewState:j_id1:j_id2
Попробуем решить задачу в лоб и отправить эти данные простым массивом. Сразу учтем, что данные отправляются на защищенный ssl url, по протоколу https. Это следует учитывать при отправке данных в cUrl. Рассказывать, как работает SSL в рамках данной статьи я не стану, скажу лишь, что сертификаты, которыми обмениваются две стороны, сами по себе ничего не говорят о его обладателе. Они нужны лишь для того, чтобы передать открытый ключ, обеих сторон и служат для шифрования канала связи. Т.е. мы можем проверять наличие ssl, и подключать сертификат в cUrl, но это будет служить пользу только нам, сервер не может узнать пользуемся ли мы сертификатом, или используем незащищенный канал связи.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//О том, что мы авторизовались будем судить по наличию формы logout
function isAuth( $data ){
  return preg_match('#<form[^>]+id="logout"#Usi',$data);
}
$ch = curl_init();
$url 'https://tender.me.gov.ua/EDZFrontOffice/menu/uk/';
curl_setopt($ch, CURLOPT_URL, $url ); // отправляем на
curl_setopt($ch, CURLOPT_HEADER, 0); // пустые заголовки
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // возвратить то что вернул сервер
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // следовать за редиректами
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);// таймаут4
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// просто отключаем проверку сертификата
curl_setopt($ch, CURLOPT_POST, 1); // использовать данные в post
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
  'login'=>'login',
  'login:login'=>'mylogin@sitename.ru',
  'login:password'=>'password',
  'login:j_id_id254.x'=>8,
  'login:j_id_id254.y'=>7,
  'javax.faces.ViewState'=>'j_id1:j_id2',
));
echo isAuth($data = curl_exec($ch))?'Success':'Failed';
curl_close($ch);
Запускаем и видим, что ничего не получилось. Сайт просто так не сдался. Мы не учли одну вещь, данные об авторизации должны быть сохранены в cookie. Для этого добавим две строчки в код, к примеру после curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1
2
3
4
5
...
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_COOKIEJAR, dirname(__FILE__).'/cookie.txt'); // сохранять куки в файл
curl_setopt($ch, CURLOPT_COOKIEFILE,  dirname(__FILE__).'/cookie.txt');
...
И снова мимо. Обратим внимание, что форма помимо стандартных логина-пароля, отправляет еще 3 динамических поля. Данные в них все время разные и генерируются при обновлении страницы. Значит, нужно загрузить страницу, скопировать эти данные и проходить аутентификацию уже с ними. Для этого немного "облагородим" код, заключив запрос данных с сервера cUrl'ом в отдельную функцию. Все отличие get запроса от post, заключается в том, что при пост запросе отправляются данные CURLOPT_POSTFIELDS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function request($url,$post = 0){
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url ); // отправляем на
  curl_setopt($ch, CURLOPT_HEADER, 0); // пустые заголовки
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // возвратить то что вернул сервер
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // следовать за редиректами
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);// таймаут4
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  curl_setopt($ch, CURLOPT_COOKIEJAR, dirname(__FILE__).'/cookie.txt'); // сохранять куки в файл
  curl_setopt($ch, CURLOPT_COOKIEFILE,  dirname(__FILE__).'/cookie.txt');
  curl_setopt($ch, CURLOPT_POST, $post!==0 ); // использовать данные в post
  if($post)
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  $data = curl_exec($ch);
  curl_close($ch);
  return $data;
}
Попытаемся выдернуть значения переменных полей 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$data = request('https://tender.me.gov.ua/EDZFrontOffice/');
include 'simple_html_dom.php';
$data = str_get_html($data);
$auth array(
    'login'=>'login',
    'login:login'=>'***************',
    'login:password'=>'************l',
    'login:j_id_id254.x'=>$data->find('input[name="login:j_id_id254.x"]',0)->value,
    'login:j_id_id254.y'=>$data->find('input[name="login:j_id_id254.y"]',0)->value,
    'javax.faces.ViewState'=>$data->find('input[name="javax.faces.ViewState"]',0)->value,
);
$data->clear();
unset($data);
print_r($auth);
Оказывается, что поле login:j_id_id254.x и поле login:j_id_id254.y создаются и заполняются js скриптом. Попробуем оставить их как были 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$url 'https://tender.me.gov.ua/EDZFrontOffice/menu/uk/';
$data = request('https://tender.me.gov.ua/EDZFrontOffice/');
include 'simple_html_dom.php';
$data = str_get_html($data);
$auth array(
    'login'=>'login',
    'login:login'=>'site@sitename.ru',
    'login:password'=>'password',
    'login:j_id_id254.x'=>10,
    'login:j_id_id254.y'=>11,
    'javax.faces.ViewState'=>$data->find('input[name="javax.faces.ViewState"]',0)->value,
);
$data->clear();
unset($data);
echo isAuth(request($url,$auth))?'Success':'Failed';;
Вот  и все, сайт побежден. Далее работаем с ним, как с обычным сайтом. Данные о том, что Ваш скрипт никто иной, как авторизованный пользователь бережно хранятся в cookie.   

JavaScript learn

Чтобы вставить элемент после какого-то элемента, нужно создать прототип. Element.prototype.appendAfter = function (element) { element.paren...