Advertisement
Guest User

irbis64 php

a guest
Aug 18th, 2016
516
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 17.44 KB | None | 0 0
  1. <?php
  2. session_start();
  3. error_reporting(E_ALL);
  4.  
  5. ////////////////////////////////////////////////////////////////////////////////
  6. class irbis64 {
  7.     private $ip = '', $port = '', $sock;
  8.     private $login = '', $pass = '';
  9.     private $id = '', $seq = 1;
  10.  
  11.     public $arm = 'C'; // Каталогизатор
  12.     public $db = 'IBIS'; //
  13.     public $server_timeout = 30;
  14.     public $server_ver     = '';
  15.     public $error_code = 0;
  16.  
  17.     function __construct() { }
  18.     function __destruct() { $this->logout(); }
  19.  
  20.     function set_server($ip, $port = 6666) { $this->ip = $ip; $this->port = (int)$port; }
  21.     function set_user($login, $pass) { $this->login = $login; $this->pass = $pass; }
  22.     function set_arm($arm) { $this->arm = $arm; }
  23.     function set_db($db) { $this->db = $db; }
  24.     function set_id($id) { $this->id = $id; }
  25.  
  26.     function error($code = '') {
  27.         if ($code == '') $code = $this->error_code;
  28.        
  29.         switch ($code) {
  30.         case '0': return 'Ошибки нет';
  31.         case '1': return 'Подключение к серверу не удалось';
  32.  
  33.         case '-3333': return 'Пользователь не существует';
  34.         case '-3335': return 'Неправильный уникальный идентификатор';
  35.         case '-3337': return 'Пользователь уже зарегистрирован';
  36.         case '-4444': return 'Пароль не подходит';
  37.  
  38.         case '-140':  return 'MFN за пределами базы';
  39.         case '-5555': return 'База не существует';
  40.         case '-400':    return 'Ошибка при открытии файла mst или xrf';
  41.         case '-603':    return 'Запись логически удалена';
  42.         case '-601':    return 'Запись удалена';
  43.  
  44.         case '-202':    return 'Термин не существует';
  45.         case '-203':    return 'TERM_LAST_IN_LIST';
  46.         case '-204':    return 'TERM_FIRST_IN_LIST';
  47.  
  48.         case '-608':    return 'Не совпадает номер версии у сохраняемой записи';
  49.         }
  50.         return 'Неизвестная ошибка: ' . $code;
  51.     }
  52.  
  53.     function connect() {
  54.         $this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  55.         if ($this->sock === false) return false;
  56.         return (@socket_connect($this->sock, $this->ip, $this->port));
  57.     }
  58.  
  59.     // Авторизация
  60.     function login() {
  61.         $packet = implode("\n", array('A', $this->arm, 'A', $this->id, $this->seq, '', '', '', '', '', $this->login, $this->pass));
  62.         $packet = strlen($packet)."\n".$packet;
  63.         $answer = $this->send($packet);
  64.         if ($answer === false) {
  65.             $this->error_code = 1;
  66.             return false;
  67.         }
  68.        
  69.         $this->error_code = $answer[10];
  70.         if ($this->error_code != 0) return false;
  71.         $this->server_timeout = $answer[11];
  72.         $this->server_ver     = $answer[4];
  73.         return true;
  74.     }
  75.  
  76.     // Завершение сессии
  77.     function logout() {
  78.         $packet = implode("\n", array('B', $this->arm, 'B', $this->id, $this->seq, '', '', '', '', '', $this->login));
  79.         $packet = strlen($packet) . "\n" . $packet;
  80.         $answer = $this->send($packet);
  81.         if ($answer === false) return false;
  82.  
  83.         $this->error_code = $answer[10];
  84.         if ($this->error_code != 0) return false;
  85.         return true;
  86.     }
  87.  
  88.     // Получить максимальный MFN в базе
  89.     function mfn_max() {
  90.         $packet = implode("\n", array('O', $this->arm, 'O', $this->id, $this->seq, '', '', '', '', '', $this->db));
  91.         $packet = strlen($packet) . "\n" . $packet;
  92.         $answer = $this->send($packet);
  93.         if ($answer === false) return false;
  94.  
  95.         $this->error_code = $answer[10];
  96.         if ($this->error_code > 0) {
  97.             $this->error_code = 0;
  98.             return $answer[10];
  99.         } else {
  100.             return false;
  101.         }
  102.     }
  103.  
  104.     // Чтение словаря
  105.     function terms_read($term, $num_terms = '', $format = '') {
  106.         // см. инструкцию сервера "7.8   Функции работы со словарем базы данных"
  107.         // если указан формат, по в результат добавляются по одной записи для каждой строки словаря
  108.         $packet = implode("\n", array('H', $this->arm, 'H', $this->id, $this->seq, '', '', '', '', '', $this->db, $term, $num_terms, $format));
  109.         $packet = strlen($packet) . "\n" . $packet;
  110.  
  111.         $answer = $this->send($packet);
  112.         if ($answer === false) return false;
  113.        
  114.         $this->error_code = $answer[10];
  115.        
  116.         if ($this->error_code == 0 || $this->error_code == -202) {
  117.             // массив $terms
  118.             $terms = array();
  119.             $c = count($answer) - 1;
  120.             for ($i = 11; $i < $c; $i++) {
  121.                 $terms[] = $answer[$i]; // формат ЧИСЛО_ССЫЛОК#ТЕРМИН=ЗНАЧЕНИЕ
  122.             }
  123.             return $terms;
  124.         } else return false;
  125.     }
  126.  
  127.     // Получить список ссылок термина
  128.     function term_records($term, $num_postings = '', $first_posting = '') {
  129.         // $term - список терминов для поиска. формат: "K=фантастика\nK=природа" = вывести список соответствующих хотя бы одному из терминов
  130.         // $num_postings = количество возвращаемых записей из списка, если = 0 то возвращается MAX_POSTINGS_IN_PACKET записей
  131.         // если $first_posting = 0 - возвращается только количество записей, если больше - указывает смещение первой возвращаемой записи из списка
  132.         $first_posting = (int)$first_posting;
  133.  
  134.         $packet = implode("\n", array('I', $this->arm, 'I', $this->id, $this->seq, '', '', '', '', '', $this->db, $num_postings, $first_posting, '', $term));
  135.         $packet = strlen($packet) . "\n" . $packet;
  136.  
  137.         $answer = $this->send($packet);
  138.         if ($answer === false) return false;
  139.        
  140.         $this->error_code = $answer[10];
  141.        
  142.         /* основной формат для результатов поиска
  143.                 MFN#TAG#OCC#CNT (см. инструкцию к серверу "6.5.3.1  Обыкновенный формат записи IFP")
  144.                     MFN – номер записи;
  145.                     TAG – идентификатор поля назначенный при отборе терминов в словарь;
  146.                     OCC – номер повторения;
  147.                     CNT – номер термина в поле.
  148.         */
  149.        
  150.         if ($this->error_code == 0) {
  151.             $records = array();
  152.            
  153.             $c = count($answer) - 1;
  154.             for ($i = 11; $i < $c; $i++) {
  155.                 $ret = explode('#', $answer[$i]);
  156.                 // для упрощения возвращаем только список MFN
  157.                 // или количество найденных записей (при $first_posting == 0)
  158.                 $records[] = $ret[0];
  159.             }
  160.             return $records;
  161.         } else return false;
  162.     }
  163.  
  164.     // Получить запись
  165.     function record_read($mfn, $lock = false) {
  166.         /*
  167.             record (
  168.                 mfn
  169.                 status - состояние записи
  170.                 ver - версия записи
  171.                 fields - массив полей записи в формате [номер][повторение] = значение
  172.             )  
  173.         */
  174.         $packet = implode("\n", array('C', $this->arm, 'C', $this->id, $this->seq, '', '', '', '', '', $this->db, $mfn, $lock ? 1 : 0));
  175.         $packet = strlen($packet) . "\n" . $packet;
  176.  
  177.         $answer = $this->send($packet);
  178.         if ($answer === false) return false;
  179.  
  180.         $this->error_code = $answer[10];
  181.         if ($this->error_code != 0) return false;
  182.  
  183.         $mfn_status  = explode('#', $answer[11]);
  184.         $rec_version = explode('#', $answer[12]);
  185.         $record = array(
  186.             'mfn' => $mfn_status[0],
  187.             'status' => (isset($mfn_status[1]) && $mfn_status[1] != '') ? $mfn_status[1] : 0,
  188.             'ver' => isset($rec_version[1]) ? $rec_version[1] : 0
  189.         );
  190.  
  191.         $record['fields'] = array();
  192.         $c = count($answer) - 1;
  193.         for ($i = 13; $i < $c; $i++) {
  194.             preg_match("/(\d+?)#(.*?)/U", $answer[$i], $matches);
  195.             $field_num = (int)$matches[1];
  196.             $field_val = $matches[2];
  197.             $record['fields'][$field_num][] = $field_val;
  198.         }
  199.         return $record;
  200.     }
  201.  
  202.     // Сохранить запись
  203.     function record_write($record, $lock = false, $if_update = true) { // $lock  = блокировать запись , $if_update = актуализировать запись
  204.         $fields = array();
  205.         foreach ($record['fields'] as $field_num => $field_values) {
  206.             foreach ($field_values as $field_value) {
  207.                 $fields[] = $field_num . '#' . $field_value;
  208.             }
  209.         }
  210.         $status = 0;
  211.         $records = $record['mfn'] . '#' . $record['status'] . "\x1f\x1e" . '0#' . $record['ver'] . "\x1f\x1e" . implode("\x1f\x1e", $fields) . "\x1f\x1e";
  212.  
  213.         $packet = implode("\n", array('D', $this->arm, 'D', $this->id, $this->seq, '', '', '', '', '', $this->db, $lock ? 1 : 0, $if_update ? 1 : 0, $records));
  214.         $packet = strlen($packet) . "\n" . $packet;
  215.  
  216.         $answer = $this->send($packet);
  217.         if ($answer === false) return false;
  218.        
  219.         $ret = explode('#', $answer[11]);
  220.  
  221.         $this->error_code = $ret[0];
  222.         return ($this->record_read($record['mfn']));
  223.     }
  224.  
  225.     // Поиск записей по запросу
  226.     function records_search($search_exp, $num_records = 1, $first_record = 0, $format = '@brief', $min = '', $max = '', $expression = '') {
  227.         // $search_exp = выражение для прямого поиска
  228.         //      IBIS "I=шифр документа"
  229.         //      IBIS "MHR=место хранения экз-ра"
  230.         //      IBIS "K=ключевые слова"
  231.         //      RDR "A=фио читателя"
  232.  
  233.         // $num_records = ограничение количества выдаваемых записей
  234.         // 0 - возвращается количество записей не больше MAX_POSTINGS_IN_PACKET
  235.  
  236.         // $first_record = задает смещение с какой записи возвращать результаты
  237.         // 0 - возвращается только количество найденных записей
  238.  
  239.         // $format = @ - оптимизированный (см. описание сервера "7.9.1 Поиск записей по заданному поисковому выражению (K)")
  240.         // $format = '@brief' - оптимизированный сокращенный формат (см. BRIEF.PFT - выводится в список записей в окне каталогизатора)
  241.        
  242.         // $min, $max, $expression - для последовательного поиска. $expression = условие отбора
  243.  
  244.         $packet = implode("\n", array('K', $this->arm, 'K', $this->id, $this->seq, '', '', '', '', '', $this->db, $search_exp, $num_records, $first_record, $format, $min, $max, $expression));
  245.         $packet = strlen($packet) . "\n" . $packet;
  246.  
  247.         // <debug>
  248.     //  echo 'Поиск записей по ключу ' . $search_exp . PHP_EOL;
  249.     //  if ($expression != '') echo 'Уточняющее условие ' . $expression . PHP_EOL;
  250.         // </debug>
  251.  
  252.         $answer = $this->send($packet);
  253.         if ($answer === false) return false;
  254.  
  255.         $this->error_code = $answer[10];
  256.         if ($this->error_code != 0) return false;
  257.  
  258.         $ret['found'] = $answer[11]; // количество найденных записей
  259.  
  260.         $c = count($answer) - 1;
  261.         for ($i = 12; $i < $c; $i++) {
  262.             $ret['records'][] = $answer[$i];
  263.         }
  264.         return $ret;
  265.     }
  266.  
  267.     function send($packet, $debug = false) {
  268.         if ($this->sock === false) return false;
  269.         if (!$this->connect()) return false;
  270.         $this->seq++;
  271.  
  272.         if ($debug) echo PHP_EOL . '>> ' . str_replace("\n", '\n ', $packet) . PHP_EOL;
  273.         socket_write($this->sock, $packet, strlen($packet));
  274.         $answer = '';
  275.         while ($buf = @socket_read($this->sock, 2048, PHP_NORMAL_READ)) {
  276.             $answer .= $buf;
  277.         }
  278.         socket_close($this->sock);
  279.         if ($debug) echo '<< ' . str_replace("\r\n", '\r\n ', $answer) . PHP_EOL;
  280.         return explode("\r\n", $answer);
  281.     }
  282.  
  283.     // Раскодировать строку поля на ассоциированный массив с подполями
  284.     function parse_field(&$field) {
  285.         $ret = array();
  286.         $matches = explode('^', $field);
  287.         if (count($matches) == 1) {
  288.             $matches = explode("\x1f", $field);
  289.         }
  290.         foreach ($matches as $match) {
  291.             $ret[(string)substr($match, 0, 1)] = substr($match, 1);
  292.         }
  293.         return $ret;
  294.     }
  295.  
  296.     // Раскодировать бинарную строку
  297.     function blob_decode($blob) {
  298.         return preg_replace_callback('/%([A-Fa-f0-9]{2})/', function ($matches) { return pack('H2', $matches[1]); }, $blob);
  299.     }
  300.  
  301.     // Закодировать бинарную строку
  302.     function blob_encode($binary) {
  303.         if (strlen($binary) > 100000) return false; // защита от дурака. размер файла не больше 100Кб
  304.  
  305.         $c = strlen($binary);
  306.         $ret = '';
  307.         // цифры и латинские цифры можно не кодировать в формат %HEX, а выводить напрямую в blob, сокращает на 25% объем данных
  308.         for ($i = 0; $i < $c; $i++) {
  309.             $n = ord(substr($binary, $i, 1));
  310.             if (($n >= 0x30 && $n <= 0x39) || ($n >= 0x41 && $n <= 0x5A) || ($n >= 0x61 && $n <= 0x7A)) {
  311.                 $ret .= substr($binary, $i, 1);
  312.             } else {
  313.                 $ret .= '%' . ($n < 16 ? '0' : '') . strtoupper(dechex($n));
  314.             }
  315.         }
  316.         return $ret;
  317.     }
  318. } // class
  319.  
  320. ////////////////////////////////////////////////////////////////////////////////
  321. class ek {
  322.     public $irbis64;
  323.     private $keywords, $authors, $titles, $rzn, $shifr, $inv;
  324.     private $online;
  325.    
  326.     public function __construct() {
  327.         $this->irbis64 = new irbis64();
  328.         $this->irbis64->set_server('192.168.0.2', 6666);
  329.         $this->irbis64->set_user('1', '1');
  330.         $this->irbis64->set_arm('C');
  331.         $this->irbis64->set_db('IBIS');
  332.         $this->irbis64->set_id('123123123');
  333.  
  334.         $this->online = false;
  335.         if ($this->irbis64->connect()) {
  336.             if ($this->irbis64->login()) {
  337.                 $this->online = true;
  338.             }
  339.         }
  340.  
  341. /*      if ($login) {
  342.             $this->authors  = $this->load_dict('authors',  'A=');
  343.             $this->titles   = $this->load_dict('titles',   'T=');
  344.             $this->rzn      = $this->load_dict('rzn',      'RZN=');
  345.             $this->inv      = $this->load_dict('inv',      'IN=');
  346.             $this->shifr    = $this->load_dict('shifr',    'I=');
  347.         } else {
  348.             echo 'Подключение ИРБИС не удалось';
  349.             exit;
  350.         }
  351. */
  352.     }
  353.  
  354.     public function __destruct() {
  355.         $this->irbis64->logout();
  356.     }
  357.    
  358.     public function is_online() {
  359.         return $this->online;
  360.     }
  361.  
  362.     function load_dict($name, $prefix) {
  363. //      echo "load_dict($name, $prefix)" . PHP_EOL;
  364.         $ret = array();
  365.         $result = $this->irbis64->terms_read($prefix);
  366.         if ($result !== false) {
  367.             foreach ($result as $str) {
  368.                 list($cnt, $str) = explode('#', $str, 2);
  369.                 list($keyword, $value) = explode('=', $str, 2);
  370.                 $ret[$value] = $cnt;
  371.             }
  372.             arsort($ret);
  373. //          echo 'records = ' . count($ret) . PHP_EOL;
  374.             return $ret;
  375.         } else {
  376. //          echo $this->irbis64->error() . PHP_EOL;
  377.         }
  378.     }
  379.    
  380.     // ("T=..........$"+"T=....................$")*("A=........, ..............$")
  381.    
  382.     public function search($mode, $query, $limit = 20) {
  383.         global $place_to_mhr;
  384.         if (!isset($place_to_mhr[$_SESSION['place_id']])) return false;
  385.         $mhr = $place_to_mhr[$_SESSION['place_id']];
  386.         $query = trim($query);
  387.         $format = "v903'#'&uf('6brief')'#'(v910/)";
  388.        
  389.         // Составление запроса для последовательного поиска
  390.         switch ($mode) {
  391.         case 'inv': // По инвентарному номеру или штрихкоду (по штрихкоду точное соотв)
  392.             $query = "!(if V910^B : '$query' then '1' else if V910^H = '$query' then '1' else '0' fi fi)";
  393.             break;
  394.         case 'title': // По заглавию
  395.             preg_match_all('/\w+/iu', $query, $matches);
  396.             if (isset($matches[0])) {
  397.                 $query = '!(if ';
  398.                 foreach ($matches[0] as &$title) {
  399.                     $title = "s(V200^A, V461^C) : '$title'";
  400.                 }
  401.                 $query .= implode(' and ', $matches[0]) . " then '1' else '0' fi)";
  402.             } else {
  403.                 $query = "!(if s(V200^A, V461^C) : '$query' then '1' else '0' fi)";
  404.             }
  405.             break;
  406.         case 'author': // По автору
  407.             $query = "";
  408.             break;
  409.         case 'keywords':  // По ключевым словам
  410.             $query = "";
  411.             break;
  412.         }
  413.         // Поиск книги в электронном каталоге
  414.         $books = $this->irbis64->records_search(
  415.             '"MHR='.$mhr.'$"', $limit, 1, $format, '', '', $query);
  416.         $books['query'] = $query; // #### debug
  417.  
  418.         // Обработать результаты поиска
  419.         if (isset($books['records'])) {
  420.             $records = array();
  421.             foreach($books['records'] as &$str) {
  422.                 list($mfn, $code, $title, $exemplars) = explode('#', $str, 4);
  423.  
  424.                 $exemplars = explode("\x1f", $exemplars);
  425.                 $exemplars_found = array();
  426.                 foreach($exemplars as $exemplar) {
  427.                     if (strlen($exemplar) == 0) continue;
  428.                     $exemplar = $this->irbis64->parse_field($exemplar);
  429.                     if ($exemplar['D'] != $mhr) continue; // игнорируем экземпляры с других кафедр
  430.                     if (in_array((int)$exemplar['A'], array('2','4','6'), true)) continue; // экземпляр списан, утерян или не на учёте
  431.                    
  432.                     $exemplars_found[] = array(
  433.                         'status' => $exemplar['A'],
  434.                         'inv' => $exemplar['B'],
  435.                         'barcode' => isset($exemplar['H']) ? trim($exemplar['H'], "\x1f") : ''
  436.                     );
  437.                 }
  438.                
  439.                 $records[] = array(
  440.                     'mfn' => $mfn,
  441.                     'code' => $code,
  442.                     'title' => $title,
  443.                     'exemplars' => $exemplars_found,
  444.                 );
  445.             }
  446.             $books['records'] = $records;
  447.         }
  448.         return $books;
  449.     }
  450. }
  451.  
  452. ////////////////////////////////////////////////////////////////////////////////
  453. echo 'Connect... ';
  454. $ek = new ek();
  455. if ($ek->is_online()) {
  456.     echo 'Online!' . PHP_EOL;
  457. //  echo json_encode($ek->search($mode, $query));
  458.     echo $ek->irbis64->mfn_max();
  459. } else {
  460.     echo 'offline';
  461. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement