3
/** @noinspection PhpUnused */
4
/** @noinspection PhpUnnecessaryLocalVariableInspection */
5
/** @noinspection PhpFullyQualifiedNameUsageInspection */
10
// Простой клиент для АБИС ИРБИС64.
11
// Требует PHP 5.4 или выше.
12
// Работает с сервером ИРБИС64 2014 и выше.
17
// ВАЖНО! Предполагается, что внутренняя кодировка символов в PHP -- UTF-8
18
// И строковые литералы в PHP-файлах хранятся также в кодировке UTF-8
19
// Если это не так, возможны проблемы
24
const LOGICALLY_DELETED = 1; ///< Запись логически удалена
25
const PHYSICALLY_DELETED = 2; ///< Запись физически удалена
26
const ABSENT = 4; ///< Запись отсутствует
27
const NON_ACTUALIZED = 8; ///< Запись не актуализирована
28
const LAST_VERSION = 32; ///< Последняя версия записи
29
const LOCKED_RECORD = 64; ///< Запись заблокирована на ввод
31
// Распространённые форматы
33
const ALL_FORMAT = "&uf('+0')"; ///< Полные данные по полям
34
const BRIEF_FORMAT = '@brief'; ///< Краткое библиографическое описание
35
const IBIS_FORMAT = '@ibiskw_h'; ///< Формат IBIS (старый)
36
const INFO_FORMAT = '@info_w'; ///< Информационный формат
37
const OPTIMIZED_FORMAT = '@'; ///< Оптимизированный формат
39
// Распространённые поиски
41
const KEYWORD_PREFIX = 'K='; ///< Ключевые слова
42
const AUTHOR_PREFIX = 'A='; ///< Индивидуальный автор, редактор, составитель
43
const COLLECTIVE_PREFIX = 'M='; ///< Коллектив или мероприятие
44
const TITLE_PREFIX = 'T='; ///< Заглавие
45
const INVENTORY_PREFIX = 'IN='; ///< Инвентарный номер, штрих-код или радиометка
46
const INDEX_PREFIX = 'I='; ///< Шифр документа в базе
48
// Логические операторы для поиска
50
const LOGIC_OR = 0; ///< Только ИЛИ
51
const LOGIC_OR_AND = 1; ///< ИЛИ и И
52
const LOGIC_OR_AND_NOT = 2; ///< ИЛИ, И, НЕТ (по умолчанию)
53
const LOGIC_OR_AND_NOT_FIELD = 3; ///< ИЛИ, И, НЕТ, И (в поле)
54
const LOGIC_OR_AND_NOT_PHRASE = 4; ///< ИЛИ, И, НЕТ, И (в поле), И (фраза)
58
const ADMINISTRATOR = 'A'; ///< Адмнистратор
59
const CATALOGER = 'C'; ///< Каталогизатор
60
const ACQUSITIONS = 'M'; ///< Комплектатор
61
const READER = 'R'; ///< Читатель
62
const CIRCULATION = 'B'; ///< Книговыдача
63
const BOOKLAND = 'B'; ///< Книговыдача
64
const PROVISION = 'K'; ///< Книгообеспеченность
66
// Команды глобальной корректировки
68
const ADD_FIELD = 'ADD'; ///< добавление нового повторения поля или подполя в заданное существующее поле
69
const DELETE_FIELD = 'DEL'; ///< удаляет поле или подполе в поле
70
const REPLACE_FIELD = 'REP'; ///< замена целиком поля или подполя
71
const CHANGE_FIELD = 'CHA'; ///< замена данных в поле или в подполе
72
const CHANGE_WITH_CASE = 'CHAC'; ///< замена данных в поле или в подполе с учетом регистра символов
73
const DELETE_RECORD = 'DELR'; ///< удаляет записи, поданные на корректировку
74
const UNDELETE_RECORD = 'UNDELR'; ///< восстанавливает записи
75
const CORRECT_RECORD = 'CORREC'; ///< вызывает на корректировку другие записи, отобранные по поисковым терминам из текущей или другой, доступной в системе, базы данных
76
const CREATE_RECORD = 'NEWMFN'; ///< создание новой записи в текущей или другой базе данных
77
const EMPTY_RECORD = 'EMPTY'; ///< очищает (опустошает) текущую запись
78
const UNDO_RECORD = 'UNDOR'; ///< переход к одной из предыдущих копий записи (откат)
79
const GBL_END = 'END'; ///< закрывающая операторная скобка
80
const GBL_IF = 'IF'; ///< логическое ветвление
81
const GBL_FI = 'FI'; ///< закрывающий оператор для ветвления
82
const GBL_ALL = 'ALL'; ///< дополняет записи всеми полями текущей записи
83
const GBL_REPEAT = 'REPEAT'; ///< цикл из группы операторов
84
const GBL_UNTIL = 'UNTIL'; ///< закрывающий оператор для цикла
85
const PUTLOG = 'PUTLOG'; ///< формирование пользовательского протокола
87
//Работа через WebToIrbisServer
88
const IRBIS_START_REQUEST = 'IRBIS_START_REQUEST'; //служебное слово для обозначения начала ответа/запроса
89
const IRBIS_END_REQUEST = 'IRBIS_END_REQUEST'; //служебное слово для обозначения начала ответа/запроса
92
* @brief Разделитель строк в ИРБИС.
94
const IRBIS_DELIMITER = "\x1F\x1E";
97
* @brief Короткие версии разделителя строк ИРБИС.
99
const SHORT_DELIMITER = "\x1E";
100
const ALT_DELIMITER = "\x1F";
103
* @brief Пустая ли данная строка?
105
* @param string $text Строка для изучения.
108
function is_null_or_empty($text)
110
return (!isset($text) || !$text || !trim($text));
111
} // function is_null_or_empty
114
* @brief Строки совпадают с точностью до регистра символов?
116
* @param string $str1 Первая строка.
117
* @param string $str2 Вторая строка.
120
function same_string($str1, $str2)
122
return strcasecmp($str1, $str2) === 0;
123
} // function same_string
126
* @brief Безопасное получение элемента массива по индексу.
128
* @param array $a Массив.
129
* @param int $ofs Индекс.
132
function safe_get(array $a, $ofs)
134
if (isset($a[$ofs])) {
138
} // function safe_get
141
* Проверяет, начинается ли строка с указанного фрагмента.
142
* @param string $haystack Строка, подлежащая проверке.
143
* @param string $needle Искомый фрагмент.
144
* @return bool Результат проверки.
146
function startsWith($haystack, $needle) {
147
$length = strlen ($needle);
148
return substr ($haystack, 0, $length) === $needle;
149
} // function startsWith
152
* Проверяет, заканчивается ли строка указанным фрагментом.
153
* @param string $haystack Строка, подлежащая проверке.
154
* @param string $needle Искомый фрагмент.
155
* @return bool Результат проверки.
157
function endsWith($haystack, $needle) {
158
$length = strlen($needle);
162
return substr($haystack, -$length) === $needle;
163
} // function endsWith
166
* @brief Таблица ручной перекодировки из CP1251 в UTF8.
169
function getAnsiTable()
171
static $_ansiTable = array
173
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
174
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
175
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
176
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
177
67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
178
83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
179
99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
180
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
181
125, 126, 127, 1026, 1027, 8218, 1107, 8222, 8230, 8224, 8225,
182
8364, 8240, 1033, 8249, 1034, 1036, 1035, 1039, 1106, 8216, 8217,
183
8220, 8221, 8226, 8211, 8212, 152, 8482, 1113, 8250, 1114, 1116,
184
1115, 1119, 160, 1038, 1118, 1032, 164, 1168, 166, 167, 1025, 169,
185
1028, 171, 172, 173, 174, 1031, 176, 177, 1030, 1110, 1169, 181,
186
182, 183, 1105, 8470, 1108, 187, 1112, 1029, 1109, 1111, 1040,
187
1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051,
188
1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062,
189
1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073,
190
1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084,
191
1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095,
192
1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103
199
* @brief Ручная перекодировка текста из CP1251 в UTF-8.
202
function ansiToUtf ($text)
204
$table = getAnsiTable();
205
$length = strlen ($text);
208
for ($i = 0; $i < $length; $i++) {
209
$chr = ord ($text[$i]);
212
$result .= chr ($chr);
215
$result .= chr (($chr >> 6) | 0xC0);
216
$result .= chr (($chr & 0x3F) | 0x80);
221
} // function ansiToUtf
224
* @brief Ручная перекодировка текста из CP1251 в UTF-8.
227
function utfToAnsi ($text)
229
$table = getAnsiTable();
231
$length = strlen ($text);
235
while ($offset < $length) {
236
$chr = ord ($text[$offset++]);
238
$result .= chr ($chr);
241
if (($chr & 0xE0) === 0xC0) {
242
$wide = ($chr & 0x1F) << 6;
243
$wide |= (ord ($text[$offset++]) & 0x3F);
245
for ($i = 128; $i < 256; $i++) {
246
// начинать с 0 нет смысла, сэкономим такты процессора
247
if ($table[$i] === $wide) {
255
else if (($chr & 0xF0) === 0xE0) {
271
} // function utfToAnsi
274
* @brief Замена обычных переводов строки на ИРБИСные.
276
* @param string $text Текст для замены.
277
* @return string Текст с замененными переводами строки.
279
function dos_to_irbis($text)
281
return str_replace(array("\r\n", "\r", "\n"), array(IRBIS_DELIMITER, "0x1F", "0x1E"), $text);
282
} // function dos_to_irbis
285
* @brief Замена переводов строки с ИРБИСных на обычные.
287
* @param string $text Текст для замены.
288
* @return string Текст с замененными переводами строки.
290
function irbis_to_dos($text)
292
return str_replace(IRBIS_DELIMITER, "\n", $text);
293
} // function irbis_to_dos
296
* @brief Разбивка текста на строки по ИРБИСным разделителям.
298
* @param string $text Текст для разбиения.
299
* @return array Массив строк.
301
function irbis_to_lines($text)
303
return explode(IRBIS_DELIMITER, $text);
304
} // function irbis_to_lines
307
* @brief Удаление комментариев из строки.
309
* @param string $text Текст для удаления комментариев.
310
* @return string Очищенный текст.
312
function remove_comments($text)
314
if (is_null_or_empty($text)) {
318
if (strpos($text, '/*') === false) {
325
$length = strlen($text);
327
while ($index < $length) {
343
if ($index + 1 < $length && $text[$index + 1] === '*') {
344
while ($index < $length) {
346
if ($c === "\r" || $c === "\n") {
356
} else if ($c === "'" || $c === '"' || $c === '|') {
369
} // function remove_comments
372
* @brief Подготовка динамического формата для передачи на сервер.
374
* В формате должны отсутствовать комментарии и служебные символы (например, перевод строки или табуляция).
376
* @param string $text Текст для обработки.
377
* @return string Обработанный текст.
379
function prepare_format($text)
381
$text = remove_comments($text);
382
$length = strlen($text);
388
for ($i = 0; $i < $length; $i++) {
389
if ($text[$i] < ' ') {
400
for ($i = 0; $i < $length; $i++) {
408
} // function prepare_format
411
* @brief Получение описания по коду ошибки, возвращенному сервером.
413
* @param int $code Код ошибки.
414
* @return string Словесное описание ошибки.
416
function describe_error($code)
423
-100 => 'Заданный MFN вне пределов БД',
424
-101 => 'Ошибочный размер полки',
425
-102 => 'Ошибочный номер полки',
426
-140 => 'MFN вне пределов БД',
427
-141 => 'Ошибка чтения',
428
-200 => 'Указанное поле отсутствует',
429
-201 => 'Предыдущая версия записи отсутствует',
430
-202 => 'Заданный термин не найден (термин не существует)',
431
-203 => 'Последний термин в списке',
432
-204 => 'Первый термин в списке',
433
-300 => 'База данных монопольно заблокирована',
434
-301 => 'База данных монопольно заблокирована',
435
-400 => 'Ошибка при открытии файлов MST или XRF (ошибка файла данных)',
436
-401 => 'Ошибка при открытии файлов IFP (ошибка файла индекса)',
437
-402 => 'Ошибка при записи',
438
-403 => 'Ошибка при актуализации',
439
-600 => 'Запись логически удалена',
440
-601 => 'Запись физически удалена',
441
-602 => 'Запись заблокирована на ввод',
442
-603 => 'Запись логически удалена',
443
-605 => 'Запись физически удалена',
444
-607 => 'Ошибка autoin.gbl',
445
-608 => 'Ошибка версии записи',
446
-700 => 'Ошибка создания резервной копии',
447
-701 => 'Ошибка восстановления из резервной копии',
448
-702 => 'Ошибка сортировки',
449
-703 => 'Ошибочный термин',
450
-704 => 'Ошибка создания словаря',
451
-705 => 'Ошибка загрузки словаря',
452
-800 => 'Ошибка в параметрах глобальной корректировки',
453
-801 => 'ERR_GBL_REP',
454
-802 => 'ERR_GBL_MET',
455
-1111 => 'Ошибка исполнения сервера (SERVER_EXECUTE_ERROR)',
456
-2222 => 'Ошибка в протоколе (WRONG_PROTOCOL)',
457
-3333 => 'Незарегистрированный клиент (ошибка входа на сервер) (клиент не в списке)',
458
-3334 => 'Клиент не выполнил вход на сервер (клиент не используется)',
459
-3335 => 'Неправильный уникальный идентификатор клиента',
460
-3336 => 'Нет доступа к командам АРМ',
461
-3337 => 'Клиент уже зарегистрирован',
462
-3338 => 'Недопустимый клиент',
463
-4444 => 'Неверный пароль',
464
-5555 => 'Файл не существует',
465
-6666 => 'Сервер перегружен. Достигнуто максимальное число потоков обработки',
466
-7777 => 'Не удалось запустить/прервать поток администратора (ошибка процесса)',
467
-8888 => 'Общая ошибка',
468
-100001 => 'Ошибка создания сокета',
469
-100002 => 'Сбой сети',
470
-100003 => 'Не подключен к серверу'
473
return $errors[$code] ?: 'Неизвестная ошибка';
474
} // function describe_error
477
* @brief "Хорошие" коды для readRecord.
479
* @return array "Хорошие" коды для readRecord.
481
function codes_for_read_record()
483
return array(-201, -600, -602, -603);
484
} // function codes_for_read_record
487
* @brief "Хорошие" коды для readTerms.
489
* @return array "Хорошие" коды для readTerms.
491
function codes_for_read_terms()
493
return array(-202, -203, -204);
494
} // function codes_for_read_terms
497
* @brief Специфичное для ИРБИС исключение.
499
final class IrbisException extends \Exception
502
* @brief Конструктор.
504
* @param string $message Сообщение об ошибке.
505
* @param int $code Код ошибки.
506
* @param mixed $previous Вложенное исключение.
508
public function __construct($message = "",
512
parent::__construct($message, $code, $previous);
513
} // function __construct
516
* @return string Текстовое представление исключения.
518
public function __toString()
520
return __CLASS__ . ": [$this->code]: $this->message\n";
521
} // function __toString
522
} // class IrbisException
525
* @brief Подполе записи. Состоит из кода и значения.
530
* @var string Код подполя.
535
* @var string Значение подполя.
540
* @brief Конструктор подполя.
542
* @param string $code Код подполя.
543
* @param string $value Значение подполя.
545
public function __construct($code = '', $value = '')
548
$this->value = $value;
549
} // function __construct
552
* @brief Клонирование подполя.
554
public function __clone()
556
$this->value = str_repeat($this->value, 1);
557
} // function __clone
560
* @brief Декодирование подполя из протокольного представления.
562
* @param string $line
564
public function decode($line)
566
$this->code = $line[0];
567
$this->value = substr($line, 1);
571
* @brief Верификация подполя.
573
* @param bool $throw Бросать ли исключение при ошибке?
574
* @return bool Результат верификации.
575
* @throws IrbisException
577
public function verify($throw = true)
579
$result = $this->code && $this->value;
580
if (!$result && $throw) {
581
throw new IrbisException();
587
public function __toString()
589
return '^' . $this->code . $this->value;
590
} // function __toString
594
* @brief Поле записи. Состоит из метки и (опционального) значения.
596
* Может содержать произвольное количество подполей.
598
final class RecordField
601
* @var int Метка поля.
606
* @var string Значение поля до первого разделителя.
611
* @var array Массив подполей.
613
public $subfields = array();
616
* @brief Конструктор поля.
618
* @param int $tag Метка поля.
619
* @param string $value Значение поля.
621
public function __construct($tag = 0, $value = '')
624
$this->value = $value;
625
} // function __construct
628
* @brief Клонирование поля.
630
public function __clone()
632
$this->value = str_repeat($this->value, 1);
634
foreach ($this->subfields as $i => $subfield) {
635
$new[$i] = clone $subfield;
637
$this->subfields = $new;
638
} // function __clone
641
* @brief Добавление подполя с указанными кодом и значением.
643
* @param string $code Код подполя.
644
* @param string $value Значение подполя.
647
public function add($code, $value)
649
$subfield = new SubField();
650
$subfield->code = $code;
651
$subfield->value = $value;
652
$this->subfields[] = $subfield;
658
* @brief Очищает поле (удаляет значение и все подполя).
662
public function clear()
665
$this->subfields = array();
671
* @brief Декодирование поля из протокольного представления.
673
* @param string $line
675
public function decode($line)
677
$this->tag = (int) strtok($line, "#");
680
if ($body[0] === '^') {
682
$all = explode('^', $body);
684
$this->value = strtok($body, '^');
685
$all = explode('^', strtok(''));
688
foreach ($all as $one) {
690
$sf = new SubField();
692
$this->subfields[] = $sf;
698
* @brief Получает массив встроенных полей из данного поля.
700
* @return array Встроенные поля.
702
public function getEmbeddedFields()
706
foreach ($this->subfields as $subfield) {
707
if ($subfield->code === '1') {
709
if (count($found->subfields) || $found->value) {
714
$value = $subfield->value;
718
$tag = intval(substr($value, 0, 3));
719
$found = new RecordField($tag);
721
$found->value = substr($value, 3);
725
$found->subfields[] = $subfield;
731
if (count($found->subfields) || $found->value) {
737
} // function getEmbeddedFields
740
* @brief Возвращает первое вхождение подполя с указанным кодом.
742
* @param string $code Код искомого подполя.
743
* @return SubField|null Найденное подполе.
745
public function getFirstSubfield($code)
747
foreach ($this->subfields as $subfield) {
748
if (same_string($subfield->code, $code)) {
754
} // function getFirstSubfield
757
* @brief Возвращает значение первого вхождения подполя с указанным кодом.
759
* @param string $code Код искомого подполя.
760
* @return string Значение найденного подполя либо пустая строка.
762
public function getFirstSubfieldValue($code)
764
foreach ($this->subfields as $subfield) {
765
if (same_string($subfield->code, $code)) {
766
return $subfield->value;
771
} // function getFirstSubfieldValue
774
* @brief Вставляет подполе по указанному индексу.
776
* @param int $index Позиция для вставки.
777
* @param SubField $subfield Подполе.
779
public function insertAt($index, SubField $subfield)
781
array_splice($this->subfields, $index, 0, $subfield);
782
} // function insertAt
785
* @brief Удаляет подполе по указанному индексу.
787
* @param int $index Индекс для удаления.
789
public function removeAt($index)
791
unset($this->subfields[$index]);
792
$this->subfields = array_values($this->subfields);
793
} // function removeAt
796
* @brief Удаляет все подполя с указанным кодом.
798
* @param string $code Искомый код подполя.
800
public function removeSubfield($code)
803
$len = count($this->subfields);
804
for ($i = 0; $i < $len; ++$i) {
805
$sub = $this->subfields[$i];
806
if (same_string($sub->code, $code)) {
807
unset($this->subfields[$i]);
812
$this->subfields = array_values($this->subfields);
814
} // function removeSubfield
817
* @brief Устанавливает значение подполя с указанным кодом.
819
* @param string $code Искомый код подполя.
820
* @param string $value Новое значение подполя.
823
public function setSubfield($code, $value)
826
$this->removeSubfield($code);
829
$subfield = $this->getFirstSubfield($code);
831
$this->add($code, $value);
832
$subfield = $this->getFirstSubfield($code);
835
$subfield->value = $value;
839
} // function setSubfield
842
* @brief Верификация поля.
844
* @param bool $throw Бросать ли исключение при ошибке?
845
* @return bool Результат верификации.
846
* @throws IrbisException Ошибка в структуре поля.
848
public function verify($throw = true)
850
$result = $this->tag && ($this->value || count($this->subfields));
851
if ($result && $this->subfields) {
852
foreach ($this->subfields as $subfield) {
853
$result = $subfield->verify($throw);
860
if (!$result && $throw) {
861
throw new IrbisException();
867
public function __toString()
869
$result = $this->tag . '#' . $this->value;
871
foreach ($this->subfields as $sf) {
876
} // function __toString
877
} // class RecordField
880
* @brief Запись. Состоит из произвольного количества полей.
882
final class MarcRecord
885
* @var string Имя базы данных, в которой хранится запись.
887
public $database = '';
890
* @var int MFN записи.
895
* @var int Версия записи.
900
* @var int Статус записи.
905
* @var array Массив полей.
907
public $fields = array();
909
public function __clone()
911
$this->database = str_repeat($this->database, 1);
913
foreach ($this->fields as $i => $field) {
914
$new[$i] = clone $field;
916
$this->fields = $new;
917
} // function __clone
920
* Добавление поля в запись.
922
* @param int $tag Метка поля.
923
* @param string $value Значение поля до первого разделителя.
924
* @return RecordField Созданное поле.
926
public function add($tag, $value = '')
928
$field = new RecordField();
930
$field->value = $value;
931
$this->fields[] = $field;
937
* Очистка записи (удаление всех полей).
941
public function clear()
943
$this->fields = array();
949
* Декодирование ответа сервера.
951
* @param array $lines Массив строк
952
* с клиентским представлением записи.
954
public function decode(array $lines)
956
if (empty($lines) || count($lines) < 2) {
960
// mfn and status of the record
961
$firstLine = explode('#', $lines[0]);
962
$this->mfn = intval($firstLine[0]);
963
$this->status = intval(safe_get($firstLine, 1));
965
// version of the record
966
$secondLine = explode('#', $lines[1]);
967
$this->version = intval(safe_get($secondLine, 1));
968
$lines = array_slice($lines, 2);
971
foreach ($lines as $line) {
973
$field = new RecordField();
974
$field->decode($line);
975
$this->fields[] = $field;
981
* Кодирование записи в протокольное представление.
983
* @param string $delimiter Разделитель строк.
984
* В зависимости от ситуации ИРБИСный или обычный.
987
public function encode($delimiter = IRBIS_DELIMITER)
989
$result = $this->mfn . '#' . $this->status . $delimiter
990
. '0#' . $this->version . $delimiter;
992
foreach ($this->fields as $field) {
993
$result .= ($field . $delimiter);
1000
* Получение значения поля (или подполя)
1001
* с указанной меткой (и указанным кодом).
1003
* @param int $tag Метка поля
1004
* @param string $code Код подполя
1005
* @return string|null
1007
public function fm($tag, $code = '')
1009
foreach ($this->fields as $field) {
1010
if ($field->tag === $tag) {
1012
foreach ($field->subfields as $subfield) {
1013
if (strcasecmp($subfield->code, $code) === 0) {
1014
return $subfield->value;
1018
return $field->value;
1027
* Получение массива значений поля (или подполя)
1028
* с указанной меткой (и указанным кодом).
1030
* @param int $tag Искомая метка поля.
1031
* @param string $code Код подполя.
1034
public function fma($tag, $code = '')
1037
foreach ($this->fields as $field) {
1038
if ($field->tag === $tag) {
1040
foreach ($field->subfields as $subfield) {
1041
if (strcasecmp($subfield->code, $code) === 0) {
1042
if ($subfield->value) {
1043
$result[] = $subfield->value;
1048
if ($field->value) {
1049
$result[] = $field->value;
1059
* Получение указанного поля (с учётом повторения).
1061
* @param int $tag Метка поля.
1062
* @param int $occurrence Номер повторения.
1063
* @return RecordField|null
1065
public function getField($tag, $occurrence = 0)
1067
foreach ($this->fields as $field) {
1068
if ($field->tag === $tag) {
1078
} // function getField
1081
* Получение массива полей с указанной меткой.
1083
* @param int $tag Искомая метка поля.
1086
public function getFields($tag)
1089
foreach ($this->fields as $field) {
1090
if ($field->tag === $tag) {
1096
} // function getFields
1099
* Определяет, удалена ли запись?
1101
* @return bool Запись удалена
1102
* (неважно - логически или физически)?
1104
public function isDeleted()
1106
return ($this->status & 3) !== 0;
1107
} // function is_deleted
1110
* Вставляет поле по указанному индексу.
1112
* @param int $index Позиция для вставки.
1113
* @param RecordField $field Поле.
1115
public function insertAt($index, RecordField $field)
1117
array_splice($this->fields, $index, 0, $field);
1118
} // function insertAt
1121
* Удаляет поле по указанному индексу.
1123
* @param int $index Индекс для удаления.
1125
public function removeAt($index)
1127
unset($this->fields[$index]);
1128
$this->fields = array_values($this->fields);
1129
} // function removeAt
1132
* Удаляет все поля с указанной меткой.
1134
* @param int $tag Индекс для удаления.
1136
public function removeField($tag)
1139
$len = count($this->fields);
1140
for ($i = 0; $i < $len; ++$i) {
1141
$field = $this->fields[$i];
1142
if ($field->tag === $tag) {
1143
unset($this->fields[$i]);
1148
$this->fields = array_values($this->fields);
1150
} // function removeField
1153
* Сброс состояния записи, отвязка её от базы данных.
1154
* Поля данных остаются при этом нетронутыми.
1158
public function reset()
1163
$this->database = '';
1169
* @brief Установка значения поля до первого разделителя.
1171
* @param int $tag Искомая метка поля.
1172
* @param string $value Новое значение до первого разделителя.
1175
public function setValue($tag, $value)
1178
$this->removeField($tag);
1181
$field = $this->getField($tag);
1183
$field = $this->add($tag);
1186
$field->value = $value;
1190
} // function setValue
1193
* @brief Установка значения подполя.
1195
* @param int $tag Метка поля.
1196
* @param string $code Искомый код подполя.
1197
* @param string $value Новое значение подполя.
1200
public function setSubfield($tag, $code, $value)
1202
$field = $this->getField($tag);
1205
$field->removeSubfield($code);
1210
$field = $this->add($tag);
1213
$field->setSubfield($code, $value);
1217
} // function setSubfield
1220
* Верификация записи.
1222
* @param bool $throw Бросать ли исключение при ошибке?
1223
* @return bool Результат верификации.
1225
public function verify($throw = true)
1228
foreach ($this->fields as $field) {
1229
$result = $field->verify($throw);
1236
} // function verify
1238
public function __toString()
1240
return $this->encode();
1241
} // function __toStirng
1242
} // class MarcRecord
1245
* @brief Запись в "сыром" ("неразобранном") виде.
1247
final class RawRecord
1250
* @var string Имя базы данных.
1252
public $database = '';
1260
* @var string Статус.
1262
public $status = '';
1265
* @var string Версия.
1267
public $version = '';
1270
* @var array Поля записи.
1272
public $fields = array();
1275
* Декодирование ответа сервера.
1277
* @param array $lines Массив строк
1278
* с клиентским представлением записи.
1280
public function decode(array $lines)
1282
if (empty($lines) || count($lines) < 2) {
1286
// mfn and status of the record
1287
$firstLine = explode('#', $lines[0]);
1288
$this->mfn = intval($firstLine[0]);
1289
$this->status = intval(safe_get($firstLine, 1));
1291
// version of the record
1292
$secondLine = explode('#', $lines[1]);
1293
$this->version = intval(safe_get($secondLine, 1));
1294
$this->fields = array_slice($lines, 2);
1295
} // function decode
1298
* Кодирование записи в протокольное представление.
1300
* @param string $delimiter Разделитель строк.
1301
* В зависимости от ситуации ИРБИСный или обычный.
1304
public function encode($delimiter = IRBIS_DELIMITER)
1306
$result = $this->mfn . '#' . $this->status . $delimiter
1307
. '0#' . $this->version . $delimiter;
1309
foreach ($this->fields as $field) {
1310
$result .= ($field . $delimiter);
1314
} // function encode
1318
* @brief Строка найденной записи в ответе сервера.
1320
final class FoundLine
1323
* @var bool Материализована?
1325
public $materialized = false;
1328
* @var int Порядковый номер.
1330
public $serialNumber = 0;
1340
public $icon = null;
1343
* @var bool Выбрана (помечена).
1345
public $selected = false;
1348
* @var string Библиографическое описание.
1350
public $description = '';
1353
* @var string Ключ для сортировки.
1358
* Разбор ответа сервера.
1360
* @param array $lines Строки ответа сервера.
1361
* @return array Массив найденных записей с MFN
1362
* и биб. описанием (опционально).
1364
public static function parse(array $lines)
1367
foreach ($lines as $line) {
1368
$parts = explode('#', $line, 2);
1369
$item = new FoundLine();
1370
$item->mfn = intval($parts[0]);
1371
$item->description = safe_get($parts, 1);
1379
* Разбор ответа сервера.
1381
* @param array $lines Строки ответа сервера.
1382
* @return array Массив MFN найденных записей.
1384
public static function parseMfn(array $lines)
1387
foreach ($lines as $line) {
1388
$parts = explode('#', $line, 2);
1389
$mfn = intval($parts[0]);
1397
* Преобразование в массив библиографических описаний.
1399
* @param array $found Найденные записи.
1400
* @return array Массив описаний.
1402
public static function toDescription(array $found)
1405
foreach ($found as $item) {
1406
$result[] = $item->description;
1413
* Преобразование в массив MFN.
1415
* @param array $found Найденные записи.
1416
* @return array Массив MFN.
1418
public static function toMfn(array $found)
1421
foreach ($found as $item) {
1422
$result[] = $item->mfn;
1428
public function __toString()
1430
return $this->description
1431
? $this->mfn . '#' . $this->description
1432
: strval($this->mfn);
1437
* @brief Пара строк в меню.
1439
final class MenuEntry
1442
* @var string Код -- первая строка в меню.
1447
* @var string Соответствующее коду значение -- вторая строка в меню.
1451
public function __toString()
1453
return $this->code . ' - ' . $this->comment;
1458
* @brief Файл меню. Состоит из пар строк (см. MenuEntry).
1463
* @var array Массив пар строк.
1465
public $entries = array();
1468
* Добавление элемента.
1470
* @param string $code Код элемента.
1471
* @param string $comment Комментарий.
1474
public function add($code, $comment)
1476
$entry = new MenuEntry();
1477
$entry->code = $code;
1478
$entry->comment = $comment;
1479
$this->entries[] = $entry;
1485
* Отыскивает запись, соответствующую данному коду.
1487
* @param string $code
1488
* @return mixed|null
1490
public function getEntry($code)
1492
foreach ($this->entries as $entry) {
1493
if (strcasecmp($entry->code, $code) === 0) {
1498
$code = trim($code);
1499
foreach ($this->entries as $entry) {
1500
if (strcasecmp($entry->code, $code) === 0) {
1505
$code = self::trimCode($code);
1506
foreach ($this->entries as $entry) {
1507
if (strcasecmp($entry->code, $code) === 0) {
1516
* Выдает значение, соответствующее коду.
1519
* @param string $defaultValue
1522
public function getValue($code, $defaultValue = '')
1524
$entry = $this->getEntry($code);
1526
return $defaultValue;
1529
return $entry->comment;
1533
* Разбор серверного представления MNU-файла.
1535
* @param array $lines Массив строк.
1537
public function parse(array $lines)
1539
$length = count($lines);
1540
for ($i = 0; $i < $length; $i += 2) {
1542
if (!$code || substr($code, 5) === '*****') {
1546
$comment = $lines[$i + 1];
1547
$entry = new MenuEntry();
1548
$entry->code = $code;
1549
$entry->comment = $comment;
1550
$this->entries[] = $entry;
1555
* Отрезание лишних символов в коде.
1557
* @param string $code Код.
1558
* @return string Очищенный код.
1560
public static function trimCode($code)
1562
return trim($code, '-=:');
1565
public function __toString()
1569
foreach ($this->entries as $entry) {
1570
$result .= ($entry . PHP_EOL);
1572
$result .= "*****\n";
1579
* @brief Строка INI-файла. Состоит из ключа
1580
* и (опционального) значения.
1590
* @var string Значение.
1594
public function __toString()
1596
return $this->key . ' = ' . $this->value;
1601
* @brief Секция INI-файла. Состоит из строк
1604
final class IniSection
1607
* @var string Имя секции.
1612
* @var array Строки 'ключ=значение'.
1614
public $lines = array();
1617
* Поиск строки с указанным ключом.
1619
* @param string $key Имя ключа.
1620
* @return IniLine|null
1622
public function find($key)
1624
foreach ($this->lines as $line) {
1625
if (same_string($line->key, $key)) {
1634
* Получение значения для указанного ключа.
1636
* @param string $key Имя ключа.
1637
* @param string $defaultValue Значение по умолчанию.
1638
* @return string Найденное значение или значение
1641
public function getValue($key, $defaultValue = '')
1643
$found = $this->find($key);
1644
return $found ? $found->value : $defaultValue;
1648
* Удаление элемента с указанным ключом.
1650
* @param string $key Имя ключа.
1651
* @return IniSection
1653
public function remove($key)
1655
$length = count($this->lines);
1656
for ($i = 0; $i < $length; $i++) {
1657
if (same_string($this->lines[$i]->key, $key)) {
1658
unset($this->lines[$i]);
1667
* Установка значения.
1669
* @param string $key
1670
* @param string $value
1672
public function setValue($key, $value)
1675
$this->remove($key);
1677
$item = $this->find($key);
1679
$item->value = $value;
1681
$item = new IniLine();
1683
$item->value = $value;
1684
$this->lines[] = $item;
1689
public function __toString()
1691
$result = '[' . $this->name . ']' . PHP_EOL;
1693
foreach ($this->lines as $line) {
1694
$result .= ($line . PHP_EOL);
1699
} // class IniSection
1702
* @brief INI-файл. Состоит из секций (см. IniSection).
1707
* @var array Секции INI-файла.
1709
public $sections = array();
1712
* Поиск секции с указанным именем.
1714
* @param string $name Имя секции.
1715
* @return mixed|null
1717
public function findSection($name)
1719
foreach ($this->sections as $section) {
1720
if (same_string($section->name, $name)) {
1729
* Поиск секции с указанным именем или создание
1730
* в случае её отсутствия.
1732
* @param string $name Имя секции.
1733
* @return IniSection
1735
public function getOrCreateSection($name)
1737
$result = $this->findSection($name);
1739
$result = new IniSection();
1740
$result->name = $name;
1741
$this->sections[] = $result;
1748
* Получение значения (из одной из секций).
1750
* @param string $sectionName Имя секции.
1751
* @param string $key Ключ искомого элемента.
1752
* @param string $defaultValue Значение по умолчанию.
1753
* @return string Значение найденного элемента
1754
* или значение по умолчанию.
1756
public function getValue($sectionName, $key, $defaultValue = '')
1758
$section = $this->findSection($sectionName);
1760
return $section->getValue($key, $defaultValue);
1763
return $defaultValue;
1767
* Разбор текстового представления INI-файла.
1769
* @param array $lines Строки INI-файла.
1771
public function parse(array $lines)
1775
foreach ($lines as $line) {
1776
$trimmed = trim($line);
1777
if (is_null_or_empty($trimmed)) {
1781
if ($trimmed[0] === '[') {
1782
$name = substr($trimmed, 1, -1);
1783
$section = $this->getOrCreateSection($name);
1784
} else if ($section) {
1785
$parts = explode('=', $trimmed, 2);
1787
$value = safe_get($parts, 1);
1788
$item = new IniLine();
1790
$item->value = $value;
1791
$section->lines[] = $item;
1797
* Установка значения элемента (в одной из секций).
1799
* @param string $sectionName Имя секции.
1800
* @param string $key Ключ элемента.
1801
* @param string $value Значение элемента.
1804
public function setValue($sectionName, $key, $value)
1806
$section = $this->getOrCreateSection($sectionName);
1807
$section->setValue($key, $value);
1812
public function __toString()
1817
foreach ($this->sections as $section) {
1822
$result .= $section;
1832
* @brief Узел дерева TRE-файла.
1837
* @var array Дочерние узлы.
1839
public $children = array();
1842
* @var string Значение, хранящееся в узле.
1847
* @var int Уровень вложенности узла.
1852
* TreeNode constructor.
1853
* @param string $value
1855
public function __construct($value = '')
1857
$this->value = $value;
1861
* Добавление дочернего узла с указанным значением.
1866
public function add($value)
1868
$child = new TreeNode($value);
1869
$this->children[] = $child;
1874
public function __toString()
1876
return $this->value;
1881
* @brief Дерево, хранящееся в TRE-файле.
1886
* @var array Корни дерева.
1888
public $roots = array();
1890
private static function arrange1(array $list, $level)
1892
$count = count($list);
1895
while ($index < $count) {
1896
$next = self::arrange2($list, $level, $index, $count);
1901
private static function arrange2(array $list, $level, $index, $count)
1904
$level2 = $level + 1;
1906
$parent = $list[$index];
1907
while ($next < $count) {
1908
$child = $list[$next];
1909
if ($child->level < $level) {
1913
if ($child->level === $level2) {
1914
$parent->children[] = $child;
1923
private static function countIndent($text)
1926
$length = strlen($text);
1927
for ($i = 0; $i < $length; $i++) {
1928
if ($text[$i] === "\t") {
1939
* Добавление корневого элемента.
1941
* @param string $value Значение элемента.
1942
* @return TreeNode Созданный элемент.
1944
public function addRoot($value)
1946
$result = new TreeNode($value);
1947
$this->roots[] = $result;
1953
* Разбор ответа сервера.
1955
* @param array $lines Строки с ответом сервера.
1956
* @throws IrbisException
1958
public function parse(array $lines)
1960
if (!count($lines)) {
1967
if (self::countIndent($line) !== 0) {
1968
throw new IrbisException();
1971
$list[] = new TreeNode($line);
1972
$lines = array_slice($lines, 1);
1973
foreach ($lines as $line) {
1974
if (is_null_or_empty($line)) {
1978
$level = self::countIndent($line);
1979
if ($level > ($currentLevel + 1)) {
1980
throw new IrbisException();
1983
$currentLevel = $level;
1984
$line = substr($line, $currentLevel);
1985
$node = new TreeNode($line);
1986
$node->level = $currentLevel;
1991
foreach ($list as $item) {
1992
if ($item->level > $maxLevel) {
1993
$maxLevel = $item->level;
1997
for ($level = 0; $level < $maxLevel; $level++) {
1998
self::arrange1($list, $level);
2001
foreach ($list as $item) {
2002
if ($item->level === 0) {
2003
$this->roots[] = $item;
2010
* @brief Информация о базе данных ИРБИС.
2012
final class DatabaseInfo
2015
* @var string Имя базы данных.
2020
* @var string Описание базы данных.
2022
public $description = '';
2025
* @var int Максимальный MFN.
2030
* @var array Логически удалённые записи.
2032
public $logicallyDeletedRecords = array();
2035
* @var array Физически удалённые записи.
2037
public $physicallyDeletedRecords = array();
2040
* @var array Неактуализированные записи.
2042
public $nonActualizedRecords = array();
2045
* @var array Заблокированные записи.
2047
public $lockedRecords = array();
2050
* @var bool Признак блокировки базы данных в целом.
2052
public $databaseLocked = false;
2055
* @var bool База только для чтения.
2057
public $readOnly = false;
2059
static function parseLine($line)
2062
$items = explode(SHORT_DELIMITER, $line);
2063
foreach ($items as $item) {
2064
$result[] = intval($item);
2071
* Разбор ответа сервера (см. getDatabaseInfo).
2073
* @param array $lines Ответ сервера.
2074
* @return DatabaseInfo
2076
public static function parseResponse(array $lines)
2078
$result = new DatabaseInfo();
2079
if (!empty($lines)) {
2080
$result->logicallyDeletedRecords = self::parseLine($lines[0]);
2081
$result->physicallyDeletedRecords = self::parseLine(safe_get($lines, 1));
2082
$result->nonActualizedRecords = self::parseLine(safe_get($lines, 2));
2083
$result->lockedRecords = self::parseLine(safe_get($lines, 3));
2084
$result->maxMfn = intval(safe_get($lines, 4));
2085
$result->databaseLocked = intval(safe_get($lines, 5)) !== 0;
2092
* Получение списка баз данных из MNU-файла.
2094
* @param MenuFile $menu Меню.
2097
public static function parseMenu(MenuFile $menu)
2100
foreach ($menu->entries as $entry) {
2101
$name = $entry->code;
2102
if ($name === '*****') {
2106
$description = $entry->comment;
2108
if ($name[0] === '-') {
2109
$name = substr($name, 1);
2113
$db = new DatabaseInfo();
2115
$db->description = $description;
2116
$db->readOnly = $readOnly;
2123
public function __toString()
2127
} // class DatabaseInfo
2130
* @brief Информация о запущенном на ИРБИС-сервере процессе.
2132
final class ProcessInfo
2135
* @var string Просто порядковый номер в списке.
2137
public $number = '';
2140
* @var string С каким клиентом взаимодействует.
2142
public $ipAddress = '';
2145
* @var string Логин оператора.
2150
* @var string Идентификатор клиента.
2152
public $clientId = '';
2155
* @var string Тип АРМ.
2157
public $workstation = '';
2160
* @var string Время запуска.
2162
public $started = '';
2165
* @var string Последняя выполненная
2166
* (или выполняемая) команда.
2168
public $lastCommand = '';
2171
* @var string Порядковый номер последней команды.
2173
public $commandNumber = '';
2176
* @var string Индентификатор процесса.
2178
public $processId = '';
2181
* @var string Состояние.
2185
public static function parse(array $lines)
2188
if (empty($lines) || count($lines) < 2)
2191
$processCount = (int)$lines[0];
2192
$linesPerProcess = (int)$lines[1];
2193
if (!$processCount || !$linesPerProcess) {
2197
$lines = array_slice($lines, 2);
2198
for ($i = 0; $i < $processCount; $i++) {
2199
if (count($lines) < 10)
2202
$process = new ProcessInfo();
2203
$process->number = $lines[0];
2204
$process->ipAddress = $lines[1];
2205
$process->name = $lines[2];
2206
$process->clientId = $lines[3];
2207
$process->workstation = $lines[4];
2208
$process->started = $lines[5];
2209
$process->lastCommand = $lines[6];
2210
$process->commandNumber = $lines[7];
2211
$process->processId = $lines[8];
2212
$process->state = $lines[9];
2214
$result[] = $process;
2215
$lines = array_slice($lines, $linesPerProcess);
2221
public function __toString()
2223
return "$this->number $this->ipAddress $this->name";
2225
} // class ProcessInfo
2228
* @brief Информация о версии ИРБИС-сервера.
2230
final class VersionInfo
2233
* @var string На какое юридическое лицо приобретен сервер.
2235
public $organization = '';
2238
* @var string Собственно версия сервера. Например, 64.2008.1
2240
public $version = '';
2243
* @var int Максимальное количество подключений.
2245
public $maxClients = 0;
2248
* @var int Текущее количество подключений.
2250
public $connectedClients = 0;
2253
* Разбор ответа сервера.
2255
* @param array $lines Строки с ответом сервера.
2257
public function parse(array $lines)
2259
if (count($lines) === 3) {
2260
$this->version = $lines[0];
2261
$this->connectedClients = (int)$lines[1];
2262
$this->maxClients = (int)$lines[2];
2264
$this->organization = $lines[0];
2265
$this->version = safe_get($lines, 1);
2266
$this->connectedClients = (int)safe_get($lines, 2);
2267
$this->maxClients = (int)safe_get($lines, 3);
2271
public function __toString()
2273
return $this->version;
2275
} // class VersionInfo
2278
* Информация о клиенте, подключенном к серверу ИРБИС
2279
* (не обязательно о текущем).
2281
final class ClientInfo
2284
* @var string Порядковый номер.
2286
public $number = '';
2289
* @var string Адрес клиента.
2291
public $ipAddress = '';
2294
* @var string Порт клиента.
2299
* @var string Логин.
2304
* @var string Идентификатор клиентской программы
2305
* (просто уникальное число).
2310
* @var string Клиентский АРМ.
2312
public $workstation = '';
2315
* @var string Момент подключения к серверу.
2317
public $registered = '';
2320
* @var string Последнее подтверждение, посланное серверу.
2322
public $acknowledged = '';
2325
* @var string Последняя команда, посланная серверу.
2327
public $lastCommand = '';
2330
* @var string Номер последней команды.
2332
public $commandNumber = '';
2335
* Разбор ответа сервера.
2337
* @param array $lines Строки ответа.
2339
public function parse(array $lines)
2341
if (empty($lines) || count($lines) < 10) {
2345
$this->number = $lines[0];
2346
$this->ipAddress = $lines[1];
2347
$this->port = $lines[2];
2348
$this->name = $lines[3];
2349
$this->id = $lines[4];
2350
$this->workstation = $lines[5];
2351
$this->registered = $lines[6];
2352
$this->acknowledged = $lines[7];
2353
$this->lastCommand = $lines[8];
2354
$this->commandNumber = $lines[9];
2357
public function __toString()
2359
return $this->ipAddress;
2361
} // class ClientInfo
2364
* Информация о зарегистрированном пользователе системы
2365
* (по данным client_m.mnu).
2370
* @var string Номер по порядку в списке.
2372
public $number = '';
2375
* @var string Логин.
2380
* @var string Пароль.
2382
public $password = '';
2385
* @var string Доступность АРМ Каталогизатор.
2387
public $cataloger = '';
2390
* @var string АРМ Читатель.
2392
public $reader = '';
2395
* @var string АРМ Книговыдача.
2397
public $circulation = '';
2400
* @var string АРМ Комплектатор.
2402
public $acquisitions = '';
2405
* @var string АРМ Книгообеспеченность.
2407
public $provision = '';
2410
* @var string АРМ Администратор.
2412
public $administrator = '';
2414
public static function formatPair($prefix, $value, $default)
2416
if (same_string($value, $default)) {
2420
return $prefix . '=' . $value . ';';
2424
* Формирование строкового представления пользователя.
2428
public function encode()
2430
return $this->name . "\r\n"
2431
. $this->password . "\r\n"
2432
. self::formatPair('C', $this->cataloger, 'irbisc.ini')
2433
. self::formatPair('R', $this->reader, 'irbisr.ini')
2434
. self::formatPair('B', $this->circulation, 'irbisb.ini')
2435
. self::formatPair('M', $this->acquisitions, 'irbism.ini')
2436
. self::formatPair('K', $this->provision, 'irbisk.ini')
2437
. self::formatPair('A', $this->administrator, 'irbisa.ini');
2441
* Разбор ответа сервера.
2443
* @param array $lines Строки ответа сервера.
2446
public static function parse(array $lines)
2449
if (empty($lines) || count($lines) < 2)
2452
$userCount = intval($lines[0]);
2453
$linesPerUser = intval($lines[1]);
2454
if (!$userCount || !$linesPerUser)
2457
$lines = array_slice($lines, 2);
2458
for ($i = 0; $i < $userCount; $i++) {
2459
if (empty($lines) || count($lines) < 9)
2462
$user = new UserInfo();
2463
$user->number = $lines[0];
2464
$user->name = $lines[1];
2465
$user->password = $lines[2];
2466
$user->cataloger = $lines[3];
2467
$user->reader = $lines[4];
2468
$user->circulation = $lines[5];
2469
$user->acquisitions = $lines[6];
2470
$user->provision = $lines[7];
2471
$user->administrator = $lines[8];
2474
$lines = array_slice($lines, $linesPerUser + 1);
2480
public function __toString()
2487
* Данные для метода printTable.
2489
final class TableDefinition
2492
* @var string Имя базы данных.
2494
public $database = '';
2497
* @var string Имя таблицы.
2502
* @var array Заголовки таблицы.
2504
public $headers = array();
2507
* @var string Режим таблицы.
2512
* @var string Поисковый запрос.
2514
public $searchQuery = '';
2517
* @var int Минимальный MFN.
2522
* @var int Максимальный MFN.
2527
* @var string Запрос для последовательного поиска.
2529
public $sequentialQuery = '';
2532
* @var array Список MFN, по которым строится таблица.
2534
public $mfnList = array();
2536
public function __toString()
2538
return $this->table;
2540
} // class TableDefinition
2543
* Статистика работы ИРБИС-сервера.
2545
final class ServerStat
2548
* @var array Подключенные клиенты.
2550
public $runningClients = array();
2553
* @var int Число клиентов, подключенных в текущий момент.
2555
public $clientCount = 0;
2558
* @var int Общее количество команд, исполненных сервером с момента запуска.
2560
public $totalCommandCount = 0;
2563
* Разбор ответа сервера.
2565
* @param array $lines Строки ответа сервера.
2567
public function parse(array $lines)
2569
if (empty($lines) || count($lines) < 2)
2572
$this->totalCommandCount = intval($lines[0]);
2573
$this->clientCount = intval($lines[1]);
2574
$linesPerClient = intval($lines[2]);
2575
if (!$linesPerClient) {
2579
$lines = array_slice($lines, 3);
2581
for ($i = 0; $i < $this->clientCount; $i++) {
2582
$client = new ClientInfo();
2583
$client->parse($lines);
2586
$this->runningClients[] = $client;
2587
$lines = array_slice($lines, $linesPerClient + 1);
2591
public function __toString()
2593
$result = strval($this->totalCommandCount) . "\n"
2594
. strval($this->clientCount) . "\n" . '8' . "\n";
2595
foreach ($this->runningClients as $client) {
2596
$result .= (strval($client) . "\n");
2601
} // class ServerStat
2604
* Параметры для запроса постингов с сервера.
2606
final class PostingParameters
2609
* @var string База данных.
2611
public $database = '';
2614
* @var int Номер первого постинга.
2616
public $firstPosting = 1;
2619
* @var string Формат.
2621
public $format = '';
2624
* @var int Требуемое количество постингов.
2626
public $numberOfPostings = 0;
2629
* @var string Термин.
2634
* @var array Список термов.
2636
public $listOfTerms = array();
2637
} // class PostingParameters
2640
* Параметры для запроса терминов с сервера.
2642
final class TermParameters
2645
* @var string Имя базы данных.
2647
public $database = '';
2650
* @var int Количество считываемых терминов.
2652
public $numberOfTerms = 0;
2655
* @var bool Возвращать в обратном порядке?
2657
public $reverseOrder = false;
2660
* @var string Начальный термин.
2662
public $startTerm = '';
2665
* @var string Формат.
2667
public $format = '';
2668
} // class TermParameters
2671
* Информация о термине поискового словаря.
2676
* @var int Количество ссылок.
2681
* @var string Поисковый термин.
2685
public static function parse(array $lines)
2688
foreach ($lines as $line) {
2689
if (!is_null_or_empty($line)) {
2690
$parts = explode('#', $line, 2);
2691
$term = new TermInfo();
2692
$term->count = intval($parts[0]);
2693
$term->text = safe_get($parts, 1);
2701
public function __toString()
2703
return $this->count . '#' . $this->text;
2708
* Постинг термина в поисковом индексе.
2710
final class TermPosting
2713
* @var int MFN записи с искомым термином.
2718
* @var int Метка поля с искомым термином.
2723
* @var int Повторение поля.
2725
public $occurrence = 0;
2728
* @var int Количество повторений.
2733
* @var string Результат форматирования.
2738
* Разбор ответа сервера.
2740
* @param array $lines Строки ответа.
2741
* @return array Массив постингов.
2743
public static function parse(array $lines)
2746
foreach ($lines as $line) {
2747
$parts = explode('#', $line, 5);
2748
if (count($parts) < 4) {
2752
$item = new TermPosting();
2753
$item->mfn = intval($parts[0]);
2754
$item->tag = intval(safe_get($parts, 1));
2755
$item->occurrence = intval(safe_get($parts, 2));
2756
$item->count = intval(safe_get($parts, 3));
2757
$item->text = safe_get($parts, 4);
2764
public function __toString()
2766
return $this->mfn . '#' . $this->tag . '#'
2767
. $this->occurrence . '#' . $this->count
2768
. '#' . $this->text;
2770
} // class TermPosting
2773
* Параметры для поиска записей (метод searchEx).
2775
final class SearchParameters
2778
* @var string Имя базы данных.
2780
public $database = '';
2783
* @var int Индекс первой требуемой записи.
2785
public $firstRecord = 1;
2788
* @var string Формат для расформатирования записей.
2790
public $format = '';
2793
* @var int Максимальный MFN.
2798
* @var int Минимальный MFN.
2803
* @var int Общее число требуемых записей.
2805
public $numberOfRecords = 0;
2808
* @var string Выражение для поиска по словарю.
2810
public $expression = '';
2813
* @var string Выражение для последовательного поиска.
2815
public $sequential = '';
2818
* @var string Выражение для локальной фильтрации.
2820
public $filter = '';
2823
* @var bool Признак кодировки UTF-8.
2825
public $isUtf = false;
2828
* @var bool Признак вложенного вызова.
2830
public $nested = false;
2831
} // class SearchParameters
2836
final class SearchScenario
2839
* @var string Название поискового атрибута (автор, инвентарный номер и т. д.).
2844
* @var string Префикс соответствующих терминов в словаре (может быть пустым).
2846
public $prefix = '';
2849
* @var int Тип словаря для соответствующего поиска.
2851
public $dictionaryType = 0;
2854
* @var string Имя файла справочника.
2856
public $menuName = '';
2859
* @var string Имя формата (без расширения).
2861
public $oldFormat = '';
2864
* @var string Способ корректировки по словарю.
2866
public $correction = '';
2869
* @var string Исходное положение переключателя "Усечение".
2871
public $truncation = '';
2874
* @var string Текст подсказки/предупреждения.
2879
* @var string Параметр пока не задействован.
2881
public $modByDicAuto = '';
2884
* @var string Применимые логические операторы.
2889
* @var string Правила автоматического расширения поиска
2890
* на основе авторитетного файла или тезауруса.
2892
public $advance = '';
2895
* @var string Имя формата показа документов.
2897
public $format = '';
2899
static function get(IniSection $section, $name, $index)
2901
$fullName = 'Item' . $name . $index;
2902
return $section->getValue($fullName);
2908
* @param IniFile $iniFile
2911
public static function parse(IniFile $iniFile)
2914
$section = $iniFile->findSection('SEARCH');
2916
$count = intval($section->getValue('ItemNumb'));
2917
for ($i = 0; $i < $count; $i++) {
2918
$scenario = new SearchScenario();
2919
$scenario->name = self::get($section, "Name", $i);
2920
$scenario->prefix = self::get($section, "Pref", $i);
2921
$scenario->dictionaryType = intval(self::get($section, "DictionType", $i));
2922
$scenario->menuName = self::get($section, "Menu", $i);
2923
$scenario->oldFormat = '';
2924
$scenario->correction = self::get($section, "ModByDic", $i);
2925
$scenario->truncation = self::get($section, "Tranc", $i);
2926
$scenario->hint = self::get($section, "Hint", $i);
2927
$scenario->modByDicAuto = self::get($section, "ModByDicAuto", $i);
2928
$scenario->logic = self::get($section, "Logic", $i);
2929
$scenario->advance = self::get($section, "Adv", $i);
2930
$scenario->format = self::get($section, "Pft", $i);
2931
$result[] = $scenario;
2937
} // class SearchScenario
2940
* PAR-файл -- содержит пути к файлам базы данных ИРБИС.
2945
// Пример файла IBIS.PAR:
2960
* @var string Путь к файлу XRF.
2965
* @var string Путь к файлу MST.
2970
* @var string Путь к файлу CNT.
2975
* @var string Путь к файлу N01.
2980
* @var string В ИРБИС64 не используется.
2985
* @var string Путь к файлу L01.
2990
* @var string В ИРБИС64 не используется.
2995
* @var string Путь к файлу IFP.
3000
* @var string Путь к файлу ANY.
3005
* @var string Путь к PFT-файлам.
3010
* @var string Расположение внешних объектов (поле 951).
3011
* Параметр появился в версии 2012.
3016
* ParFile constructor.
3017
* @param string $mst Путь к MST-файлу.
3019
public function __construct($mst = '')
3035
* Разбор ответа сервера.
3037
* @param array $lines Ответ сервера.
3038
* @throws IrbisException
3040
public function parse(array $lines)
3043
foreach ($lines as $line) {
3044
if (is_null_or_empty($line)) {
3048
$parts = explode('=', $line, 2);
3049
if (count($parts) != 2) {
3050
throw new IrbisException();
3053
$key = trim($parts[0]);
3054
$value = trim($parts[1]);
3055
$map[$key] = $value;
3058
$this->xrf = $map['1'];
3059
$this->mst = $map['2'];
3060
$this->cnt = $map['3'];
3061
$this->n01 = $map['4'];
3062
$this->n02 = $map['5'];
3063
$this->l01 = $map['6'];
3064
$this->l02 = $map['7'];
3065
$this->ifp = $map['8'];
3066
$this->any = $map['9'];
3067
$this->pft = $map['10'];
3068
$this->ext = $map['11'];
3071
public function __toString()
3073
return '1=' . $this->xrf . PHP_EOL
3074
. '2=' . $this->mst . PHP_EOL
3075
. '3=' . $this->cnt . PHP_EOL
3076
. '4=' . $this->n01 . PHP_EOL
3077
. '5=' . $this->n02 . PHP_EOL
3078
. '6=' . $this->l01 . PHP_EOL
3079
. '7=' . $this->l02 . PHP_EOL
3080
. '8=' . $this->ifp . PHP_EOL
3081
. '9=' . $this->any . PHP_EOL
3082
. '10=' . $this->pft . PHP_EOL
3083
. '11=' . $this->ext . PHP_EOL;
3084
} // function __toString()
3094
* @var string Паттерн.
3096
public $pattern = '';
3099
* @var string Соответствующий рабочий лист.
3101
public $worksheet = '';
3105
* @throws IrbisException
3107
public function parse($text)
3109
$parts = preg_split("/\s+/", trim($text), 2, PREG_SPLIT_NO_EMPTY);
3110
if (count($parts) != 2) {
3111
throw new IrbisException();
3114
$this->pattern = $parts[0];
3115
$this->worksheet = $parts[1];
3118
public function __toString()
3120
return $this->pattern . ' ' . $this->worksheet;
3121
} // function __toString
3126
* OPT-файл -- файл оптимизации рабочих листов и форматов показа.
3151
* @var int Длина рабочего листа.
3153
public $worksheetLength = 5;
3156
* @var int Метка поля рабочего листа.
3158
public $worksheetTag = 920;
3161
* @var array Строки с паттернами.
3163
public $lines = array();
3166
* Получение рабочего листа записи.
3168
* @param MarcRecord $record Запись
3169
* @return string Рабочий лист.
3171
public function getWorksheet(MarcRecord $record)
3173
return $record->fm($this->worksheetTag);
3177
* Разбор ответа сервера.
3179
* @param array $lines Строки OPT-файла.
3180
* @throws IrbisException
3182
public function parse(array $lines)
3184
if (empty($lines) || count($lines) < 2)
3185
throw new IrbisException();
3187
$this->worksheetTag = intval($lines[0]);
3188
$this->worksheetLength = intval($lines[1]);
3189
$lines = array_slice($lines, 2);
3190
foreach ($lines as $line) {
3191
if (is_null_or_empty($line)) {
3195
if ($line[0] === '*') {
3199
$item = new OptLine();
3200
$item->parse($line);
3201
$this->lines[] = $item;
3205
public static function sameChar($pattern, $testable)
3207
if ($pattern === '+') {
3211
return strtolower($pattern) === strtolower($testable);
3215
* Сопоставление строки с OPT-шаблоном.
3217
* @param string $pattern Шаблон.
3218
* @param string $testable Проверяемая строка.
3219
* @return bool Совпало?
3221
public static function sameText($pattern, $testable)
3228
return $pattern[0] === '+';
3234
$patternChar = $patternIndex < strlen($pattern)
3235
? $pattern[$patternIndex] : "\0";
3236
$testableChar = $testableIndex < strlen($testable)
3237
? $testable[$testableIndex] : "\0";
3238
$patternNext = $patternIndex++ < strlen($pattern);
3239
$testableNext = $testableIndex++ < strlen($testable);
3241
if ($patternNext && !$testableNext) {
3242
if ($patternChar === '+') {
3243
while ($patternIndex < strlen($pattern)) {
3244
$patternChar = $pattern[$patternIndex];
3246
if ($patternChar !== '+') {
3255
if ($patternNext != $testableNext) {
3259
if (!$patternNext) {
3263
if (!self::sameChar($patternChar, $testableChar)) {
3268
return false; // for PhpStorm
3272
* Подбор значения для указанного текста.
3274
* @param string $text Проверяемый текст.
3275
* @return string|null Найденное значение либо null.
3277
public function resolveWorksheet($text)
3279
foreach ($this->lines as $line) {
3280
if (self::sameText($line->pattern, $text)) {
3281
return $line->worksheet;
3288
public function __toString()
3290
$result = strval($this->worksheetTag) . PHP_EOL
3291
. strval($this->worksheetLength) . PHP_EOL;
3293
foreach ($this->lines as $line) {
3294
$result .= (strval($line) . PHP_EOL);
3297
$result .= '*****' . PHP_EOL;
3304
final class GblParameter
3307
* @var string Наименование параметра, которое появится
3308
* в названии столбца, задающего параметр.
3313
* @var string Значение параметра или пусто, если пользователю
3314
* предлагается задать его значение перед выполнением
3315
* корректировки. В этой строке можно задать имя файла
3316
* меню (с расширением MNU) или имя рабочего листа подполей
3317
* (с расширением Wss), которые будут поданы для выбора
3318
* значения параметра.
3322
} // class GblParameter
3325
* Оператор глобальной корректировки с параметрами.
3327
final class GblStatement
3330
* @var string Команда, например, ADD или DEL.
3332
public $command = '';
3335
* @var string Первый параметр, как правило, спецификация поля/подполя.
3337
public $parameter1 = '';
3340
* @var string Второй параметр, как правило, спецификация повторения.
3342
public $parameter2 = '';
3345
* @var string Первый формат, например, выражение для замены.
3347
public $format1 = '';
3350
* @var string Второй формат, например, заменяющее выражение.
3352
public $format2 = '';
3355
* GblStatement constructor.
3357
* @param string $command Команда.
3358
* @param string $parameter1 Параметр 1.
3359
* @param string $parameter2 Параметр 2.
3360
* @param string $format1 Формат 1.
3361
* @param string $format2 Формат 2.
3363
public function __construct($command,
3364
$parameter1 = 'XXXXXXXXX',
3365
$parameter2 = 'XXXXXXXXX',
3366
$format1 = 'XXXXXXXXX',
3367
$format2 = 'XXXXXXXXX')
3369
$this->command = $command;
3370
$this->parameter1 = $parameter1;
3371
$this->parameter2 = $parameter2;
3372
$this->format1 = $format1;
3373
$this->format2 = $format2;
3376
public function __toString()
3378
return $this->command . IRBIS_DELIMITER
3379
. $this->parameter1 . IRBIS_DELIMITER
3380
. $this->parameter2 . IRBIS_DELIMITER
3381
. $this->format1 . IRBIS_DELIMITER
3382
. $this->format2 . IRBIS_DELIMITER;
3385
} // class GblStatement
3388
* Настройки для глобальной корректировки.
3390
final class GblSettings
3393
* @var bool Актуализировать записи?
3395
public $actualize = true;
3398
* @var bool Запускать autoin.gbl?
3400
public $autoin = false;
3403
* @var string Имя базы данных.
3405
public $database = '';
3408
* @var string Имя файла.
3410
public $filename = '';
3413
* @var bool Применять формальный контроль?
3415
public $formalControl = false;
3418
* @var int Нижняя граница MFN для поиска обрабатываемых записей.
3420
public $lowerBound = 0;
3423
* @var int Максимальный MFN.
3428
* @var array Список MFN для обработки.
3430
public $mfnList = array();
3433
* @var int Минимальный MFN. 0 означает "все записи в базе".
3438
* @var array Параметры глобальной корректировки.
3439
* Как правило, параметров нет.
3441
public $parameters = array();
3444
* @var string Поисковое выражение отбора записей по словарю.
3446
public $searchExpression = '';
3449
* @var string Поисковое выражение последовательного поиска.
3451
public $sequentialExpression = '';
3454
* @var array Массив операторов.
3456
public $statements = array();
3459
* @var int Верхняя граница MFN для поиска обрабатываемых записей.
3461
public $upperBound = 0;
3464
* Произвести подстановку параметров (если таковые наличествуют).
3466
* @param $text string Текст, в котором должна быть произведена подстановка.
3467
* @return string Текст после подстановок.
3469
public function substituteParameters($text)
3471
$length = count($this->parameters);
3472
for ($i = 0; $i < $length; ++$i) {
3473
$mark = '%' . strval($i + 1);
3474
$text = str_replace($mark, $this->parameters[$i]->value, $text);
3480
} // class GblSettings
3483
* Клиентский запрос.
3485
final class ClientQuery
3487
private $accumulator = '';
3489
public function __construct(Connection $connection, $command)
3491
$this->addAnsi($command)->newLine();
3492
$this->addAnsi($connection->workstation)->newLine();
3493
$this->addAnsi($command)->newLine();
3494
$this->add($connection->clientId)->newLine();
3495
$this->add($connection->queryId)->newLine();
3496
$this->addAnsi($connection->password)->newLine();
3497
$this->addAnsi($connection->username)->newLine();
3504
* Добавляем целое число
3505
* (по факту выходит кодировка ANSI).
3507
* @param int $value Число.
3510
public function add($value)
3512
$this->addAnsi(strval($value));
3518
* Добавляем текст в кодировке ANSI.
3520
* @param string $value Добавляемый текст.
3523
public function addAnsi($value)
3525
$converted = utfToAnsi($value);
3526
$this->accumulator .= $converted;
3532
* Добавляем формат. Кодировка UTF8.
3534
* @param string $format Формат.
3535
* @return bool|ClientQuery
3537
public function addFormat($format)
3544
$prepared = prepare_format(ltrim($format));
3546
if ($format[0] === '@') {
3547
$this->addAnsi($format);
3548
} else if ($format[0] === '!') {
3549
$this->addUtf($prepared);
3551
$this->addUtf("!" . $prepared);
3554
return $this->newLine();
3558
* Добавляем текст в кодировке UTF-8.
3560
* @param string $value Добавляемый текст.
3563
public function addUtf($value)
3565
$this->accumulator .= $value;
3571
* Добавляем перевод строки.
3575
public function newLine()
3577
$this->accumulator .= chr(10);
3582
public function __toString()
3584
return strlen($this->accumulator) . chr(10) . $this->accumulator;
3586
} // class ClientQuery
3591
final class ServerResponse
3594
* @var string Код команды (дублирует запрос).
3596
public $command = '';
3599
* @var int Идентификатор клиента (дублирует запрос).
3601
public $clientId = 0;
3604
* @var int Номер команды (дублирует запрос).
3606
public $queryId = 0;
3609
* @var int Код возврата (бывает не у всех ответов).
3611
public $returnCode = 0;
3614
* @var int Размер ответа в байтах
3615
* (в некоторых сценариях не возвращается).
3617
public $answerSize = 0;
3620
* @var string Версия сервера
3621
* (в некоторых сценариях не возвращается).
3623
public $serverVersion = '';
3625
private $connection;
3628
private $answerLength;
3630
public function __construct(Connection $connection, $socket)
3632
$this->connection = $connection;
3635
if ($connection->webServer === false) :
3636
while ($buf = socket_read($socket, 2048)) {
3637
$this->answer .= $buf;
3641
$this->answer = str_replace(IRBIS_START_REQUEST . chr(10), null, $socket);
3642
$this->answer = str_replace(IRBIS_END_REQUEST, null, $this->answer);
3646
if ($connection->debug) {
3651
$this->answerLength = strlen($this->answer);
3653
$this->command = $this->readAnsi();
3654
$this->clientId = $this->readInteger();
3655
$this->queryId = $this->readInteger();
3656
$this->answerSize = $this->readInteger();
3657
$this->serverVersion = $this->readAnsi();
3658
for ($i = 0; $i < 5; $i++) {
3664
* Проверка кода возврата.
3666
* @param array $goodCodes Разрешенные коды возврата.
3667
* @return bool Результат проверки.
3669
public function checkReturnCode(array $goodCodes = array())
3671
if ($this->getReturnCode() < 0) {
3672
if (!in_array($this->returnCode, $goodCodes)) {
3673
$this->connection->lastError = $this->returnCode;
3681
* Отладочная печать.
3683
public function debug()
3685
file_put_contents('php://stderr', print_r($this->answer, TRUE));
3689
* Чтение строки без преобразования кодировок.
3691
* @return string Прочитанная строка.
3693
public function getLine()
3697
while ($this->offset < $this->answerLength) {
3698
$symbol = $this->answer[$this->offset];
3701
if ($symbol === chr(13)) {
3702
if ($this->answer[$this->offset] === chr(10)) {
3715
* Получение кода возврата.
3716
* Вызывается один раз в свой час и только тогда.
3717
* Отрицательное число свидетельствует о проблеме.
3719
* @return int Код возврата.
3721
public function getReturnCode()
3723
$this->returnCode = $this->readInteger();
3724
return $this->returnCode;
3728
* Чтение строки в кодировке ANSI.
3730
* @return string Прочитанная строка.
3732
public function readAnsi()
3734
$result = $this->getLine();
3735
$result = ansiToUtf($result);
3741
* Чтение целого числа.
3743
* @return int Прочитанное число.
3745
public function readInteger()
3747
$line = $this->getLine();
3749
return intval($line);
3753
* Чтение оставшихся строк в кодировке ANSI.
3757
public function readRemainingAnsiLines()
3761
while ($this->offset < $this->answerLength) {
3762
$line = $this->readAnsi();
3770
* Чтение оставшегося текста в кодировке ANSI.
3772
* @return bool|string
3774
public function readRemainingAnsiText()
3776
$result = substr($this->answer, $this->offset);
3777
$this->offset = $this->answerLength;
3778
$result = ansiToUtf($result);
3784
* Чтение оставшихся строк в кодировке UTF-8.
3788
public function readRemainingUtfLines()
3792
while ($this->offset < $this->answerLength) {
3793
$line = $this->readUtf();
3801
* Чтение оставшегося текста в кодировке UTF-8.
3803
* @return bool|string
3805
public function readRemainingUtfText()
3807
$result = substr($this->answer, $this->offset);
3808
$this->offset = $this->answerLength;
3814
* Чтение строки в кодировке UTF-8.
3818
public function readUtf()
3820
return $this->getLine();
3823
public function GetCurl()
3825
return $this->accumulator;
3827
} // class ServerResponse
3830
* Подключение к ИРБИС-серверу.
3832
final class Connection
3835
* @var string Адрес сервера (можно как my.domain.com,
3836
* так и 192.168.1.1).
3838
public $host = '127.0.0.1';
3841
* @var int Порт сервера.
3843
public $port = 6666;
3846
* @var string Логин пользователя. Регистр символов не учитывается.
3848
public $username = '';
3851
* @var string Пароль пользователя. Регистр символов учитывается.
3853
public $password = '';
3856
* @var string Имя текущей базы данных.
3858
public $database = 'IBIS';
3861
* @var string Код АРМа.
3863
public $workstation = CATALOGER;
3866
* @var int Идентификатор клиента.
3867
* Задаётся автоматически при подключении к серверу.
3869
public $clientId = 0;
3872
* @var int Последовательный номер запроса к серверу.
3873
* Ведётся автоматически.
3875
public $queryId = 0;
3878
* @var string Версия сервера (доступна после подключения).
3880
public $serverVersion = '';
3883
* @var IniFile Серверный INI-файл (доступен после подключения).
3885
public $iniFile = null;
3888
* @var int Интервал подтверждения, минуты
3889
* (доступен после подключения).
3891
public $interval = 0;
3893
private $connected = false;
3896
* @var bool Признак отладки.
3898
public $debug = false;
3901
* @var int Код последней ошибки.
3903
public $lastError = 0;
3906
* @var bool Признак использования cgi (WebToIrbisServer)
3908
public $webServer = false;
3911
* @var string Относительный путь к шлюзу
3912
* default '/cgi-bin/irbis64r_plus/WebToIrbisServer.exe'
3914
public $webCgi = '/cgi-bin/irbis64r_plus/WebToIrbisServer.exe';
3916
//================================================================
3918
function __destruct()
3920
$this->disconnect();
3923
function _checkConnection()
3925
if (!$this->connected) {
3926
$this->lastError = -100003;
3933
//================================================================
3936
* Актуализация всех неактуализированных записей
3937
* в указанной базе данных.
3939
* @param string $database Имя базы данных.
3940
* @return bool Признак успешности операции.
3942
public function actualizeDatabase($database)
3944
return $this->actualizeRecord($database, 0);
3945
} // function actualizeDatabase
3948
* Актуализация записи с указанным MFN.
3950
* @param string $database Имя базы данных.
3951
* @param int $mfn MFN, подлежащий актуализации.
3952
* @return bool Признак успешности операции.
3954
public function actualizeRecord($database, $mfn)
3956
if (!$this->_checkConnection())
3959
$query = new ClientQuery($this, 'F');
3960
$query->addAnsi($database)->newLine();
3961
$query->add($mfn)->newLine();
3962
$response = $this->execute($query);
3963
if (!$response || !$response->checkReturnCode())
3967
} // function actualizeRecord
3970
* Подключение к серверу ИРБИС64.
3972
* @return bool Признак успешности операции.
3976
if ($this->connected)
3980
$this->clientId = rand(100000, 900000);
3982
$query = new ClientQuery($this, 'A');
3983
$query->addAnsi($this->username)->newLine();
3984
$query->addAnsi($this->password);
3986
$response = $this->execute($query);
3990
$response->getReturnCode();
3991
if ($response->returnCode == -3337) {
3995
if ($response->returnCode < 0) {
3996
$this->lastError = $response->returnCode;
4000
$this->connected = true;
4001
$this->serverVersion = $response->serverVersion;
4002
$this->interval = intval($response->readUtf());
4003
$lines = $response->readRemainingAnsiLines();
4004
$this->iniFile = new IniFile();
4005
$this->iniFile->parse($lines);
4008
} // function connect
4011
* Создание базы данных.
4013
* @param string $database Имя создаваемой базы.
4014
* @param string $description Описание в свободной форме.
4015
* @param int $readerAccess Читатель будет иметь доступ?
4016
* @return bool Признак успешности операции.
4018
function createDatabase($database, $description, $readerAccess = 1)
4020
if (!$this->_checkConnection()) {
4024
$query = new ClientQuery($this, 'T');
4025
$query->addAnsi($database)->newLine();
4026
$query->addAnsi($description)->newLine();
4027
$query->add($readerAccess)->newLine();
4028
$response = $this->execute($query);
4030
return $response && $response->checkReturnCode();
4031
} // function createDatabase
4034
* Создание словаря в указанной базе данных.
4036
* @param string $database Имя базы данных.
4037
* @return bool Признак успешности операции.
4039
public function createDictionary($database)
4041
if (!$this->_checkConnection()) {
4045
$query = new ClientQuery($this, 'Z');
4046
$query->addAnsi($database)->newLine();
4047
$response = $this->execute($query);
4049
return $response && $response->checkReturnCode();
4050
} // function createDictionary
4053
* Удаление указанной базы данных.
4055
* @param string $database Имя удаляемой базы данных.
4056
* @return bool Признак успешности операции.
4058
public function deleteDatabase($database)
4060
if (!$this->_checkConnection()) {
4064
$query = new ClientQuery($this, 'W');
4065
$query->addAnsi($database)->newLine();
4066
$response = $this->execute($query);
4068
return $response && $response->checkReturnCode();
4069
} // function deleteDatabase
4072
* Удаление на сервере указанного файла.
4074
* @param string $fileName Спецификация файла.
4076
public function deleteFile($fileName)
4078
$this->formatRecord("&uf('+9K$fileName')", 1);
4079
} // function deleteFile
4082
* Удаление записи по её MFN.
4084
* @param int $mfn MFN удаляемой записи.
4085
* @return bool Признак успешности операции.
4087
public function deleteRecord($mfn)
4089
$record = $this->readRecord($mfn);
4094
if (!$record->isDeleted()) {
4095
$record->status |= LOGICALLY_DELETED;
4096
$this->writeRecord($record);
4100
} // function deleteRecord
4103
* Отключение от сервера.
4105
* @return bool Признак успешности операции.
4107
public function disconnect()
4109
if (!$this->connected) {
4113
$query = new ClientQuery($this, 'B');
4114
$query->addAnsi($this->username);
4115
if (!$this->execute($query)) {
4119
$this->connected = false;
4122
} // function disconnect
4125
* Отправка клиентского запроса на сервер
4126
* и получение ответа от него.
4128
* @param ClientQuery $query Клиентский запрос.
4129
* @return bool|ServerResponse Ответ сервера
4130
* либо признак сбоя операции.
4132
public function execute(ClientQuery $query)
4134
$this->lastError = 0;
4135
if ($this->webServer === false) {
4137
// подключение по протоколу ИРБИС-сервера
4138
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
4139
if ($socket === false) {
4140
$this->lastError = -100001;
4144
if (!socket_connect($socket, $this->host, $this->port)) {
4145
socket_close($socket);
4146
$this->lastError = -100002;
4150
$packet = strval($query);
4151
socket_write($socket, $packet, strlen($packet));
4153
file_put_contents('php://stderr', print_r($packet, TRUE));
4158
// подключение через cgi-прокси
4159
$curl = curl_init();
4160
$url = 'http://' . $this->host . $this->webCgi;
4161
curl_setopt_array($curl, array(
4162
CURLOPT_URL => $url,
4163
CURLOPT_USERAGENT => 'irbis\client',
4164
CURLOPT_RETURNTRANSFER => true,
4165
CURLOPT_HTTPHEADER => array('Accept: *.*', 'Content-Type: application/octet-stream'),
4166
CURLOPT_POST => true,
4167
CURLOPT_POSTFIELDS => strval($query->GetCurl()),
4170
$socket = curl_exec($curl);
4174
$response = new ServerResponse($this, $socket);
4178
} // function execute
4181
* Выполнение произвольной команды.
4183
* @param string $command Код команды.
4184
* @param array $params Опциональные параметры в кодировке ANSI.
4185
* @return bool|ServerResponse Ответ сервера
4186
* либо признак сбоя операции.
4188
public function executeAnyCommand($command, array $params = [])
4190
if (!$this->_checkConnection()) {
4194
$query = new ClientQuery($this, $command);
4195
foreach ($params as $param) {
4196
$query->addAnsi($param)->newLine();
4199
return $this->execute($query);
4200
} // function executeAnyCommand
4203
* Форматирование записи с указанным MFN.
4205
* @param string $format Текст формата.
4206
* @param int $mfn MFN записи.
4207
* @return bool|string Результат расформатирования
4208
* либо признак сбоя операции.
4210
public function formatRecord($format, $mfn)
4212
if (!$this->_checkConnection()) {
4216
$query = new ClientQuery($this, 'G');
4217
$query->addAnsi($this->database)->newLine();
4218
$query->addFormat($format);
4219
$query->add(1)->newLine();
4220
$query->add($mfn)->newLine();
4221
$response = $this->execute($query);
4222
if (!$response || !$response->checkReturnCode()) {
4226
return $response->readRemainingUtfText();
4227
} // function formatRecord
4230
* Форматирование записи в клиентском представлении.
4232
* @param string $format Текст формата.
4233
* @param MarcRecord $record Запись.
4234
* @return bool|string Результат расформатирования
4235
* либо признак сбоя операции.
4237
public function formatVirtualRecord($format, MarcRecord $record)
4239
if (!$this->_checkConnection()) {
4247
$query = new ClientQuery($this, 'G');
4248
$database = $record->database ?: $this->database;
4249
$query->addAnsi($database)->newLine();
4250
$query->addFormat($format);
4251
$query->add(-2)->newLine();
4252
$query->addUtf($record->encode());
4253
$response = $this->execute($query);
4254
if (!$response || !$response->checkReturnCode()) {
4258
return $response->readRemainingUtfText();
4259
} // function formatVirtualRecord
4262
* Расформатирование нескольких записей.
4264
* @param string $format Формат.
4265
* @param array $mfnList Массив MFN.
4266
* @return array|bool Результат расформатирования
4267
* либо признак сбоя операции.
4269
public function formatRecords($format, array $mfnList)
4271
if (!$this->_checkConnection())
4277
$query = new ClientQuery($this, 'G');
4278
$query->addAnsi($this->database)->newLine();
4279
if (!$query->addFormat($format))
4282
$query->add(count($mfnList))->newLine();
4283
foreach ($mfnList as $mfn)
4284
$query->add($mfn)->newLine();
4286
$response = $this->execute($query);
4287
if (!$response || !$response->checkReturnCode())
4290
$lines = $response->readRemainingUtfLines();
4292
foreach ($lines as $line) {
4293
$parts = explode('#', $line, 2);
4294
if (count($parts) == 2)
4295
$result[] = irbis_to_dos($parts[1]);
4299
} // function formatRecords
4302
* Получение информации о базе данных.
4304
* @param string $database Имя базы данных.
4305
* @return bool|DatabaseInfo Информация о базе данных
4306
* либо признак сбоя операции.
4308
public function getDatabaseInfo($database = '')
4310
if (!$this->_checkConnection())
4313
$database = $database ?: $this->database;
4314
$query = new ClientQuery($this, '0');
4315
$query->addAnsi($database);
4316
$response = $this->execute($query);
4317
if (!$response || !$response->checkReturnCode())
4320
$lines = $response->readRemainingAnsiLines();
4322
return DatabaseInfo::parseResponse($lines);
4323
} // function getDatabaseInfo
4326
* Получение максимального MFN для указанной базы данных.
4328
* @param string $database Имя базы данных.
4329
* @return int Максимальный MFN
4330
* либо 0 в качестве признака сбоя операции.
4332
public function getMaxMfn($database)
4334
if (!$this->_checkConnection()) {
4338
$query = new ClientQuery($this, 'O');
4339
$query->addAnsi($database);
4340
$response = $this->execute($query);
4341
if (!$response || !$response->checkReturnCode()) {
4345
return $response->returnCode;
4346
} // function getMaxMfn
4349
* Массив постингов для указанных записи и префикса.
4350
* @param int $mfn MFN записи.
4351
* @param string $prefix Префикс в виде "A=$".
4352
* @return array Массив TermPosting
4353
* (пустой в случае сбоя операции).
4355
public function getRecordPostings($mfn, $prefix)
4358
if (!$this->_checkConnection()) {
4362
$query = new ClientQuery($this, 'V');
4363
$query->addAnsi($this->database)->newLine();
4364
$query->add($mfn)->newLine();
4365
$query->addUtf($prefix)->newLine();
4366
$response = $this->execute($query);
4367
if (!$response || !$response->checkReturnCode()) {
4371
$lines = $response->readRemainingUtfLines();
4373
return TermPosting::parse($lines);
4374
} // function getRecordPostings
4377
* Получение статистики с сервера.
4379
* @return bool|ServerStat Статистика
4380
* либо признак сбоя операции.
4382
public function getServerStat()
4384
if (!$this->_checkConnection()) {
4388
$query = new ClientQuery($this, '+1');
4389
$response = $this->execute($query);
4390
if (!$response || !$response->checkReturnCode()) {
4394
$result = new ServerStat();
4395
$result->parse($response->readRemainingAnsiLines());
4398
} // function getServerStat
4401
* Получение версии сервера.
4403
* @return bool|VersionInfo Версия сервера
4404
* либо признак сбоя операции.
4406
public function getServerVersion()
4408
if (!$this->_checkConnection())
4411
$query = new ClientQuery($this, '1');
4412
$response = $this->execute($query);
4413
if (!$response || !$response->checkReturnCode())
4416
$result = new VersionInfo();
4417
$result->parse($response->readRemainingAnsiLines());
4420
} // function getServerVersion
4423
* Получение списка пользователей с сервера.
4425
* @return array|bool Список пользователей
4426
* либо признак сбоя операции.
4428
public function getUserList()
4430
if (!$this->_checkConnection())
4433
$query = new ClientQuery($this, '+9');
4434
$response = $this->execute($query);
4435
if (!$response || !$response->checkReturnCode())
4438
return UserInfo::parse($response->readRemainingAnsiLines());
4439
} // function getUserList
4442
* Глобальная корректировка.
4444
* @param GblSettings $settings Параметры корректировки.
4445
* @return array|bool Массив результатов корректировки
4446
* либо признак сбоя операции.
4448
public function globalCorrection(GblSettings $settings)
4450
if (!$this->_checkConnection())
4453
$query = new ClientQuery($this, '5');
4454
$database = $settings->database ?: $this->database;
4455
$query->addAnsi($database)->newLine();
4456
$query->add(intval($settings->actualize))->newLine();
4457
if (!is_null_or_empty($settings->filename)) {
4458
$query->addAnsi('@' . $settings->filename)->newLine();
4460
// "!" здесь означает, что передавать будем в UTF-8
4461
// не знаю, что тут означает "0"
4462
$encoded = '!0' . IRBIS_DELIMITER;
4463
foreach ($settings->statements as $statement) {
4464
$encoded .= $settings->substituteParameters(strval($statement));
4466
$encoded .= IRBIS_DELIMITER;
4467
$query->addUtf($encoded)->newLine();
4470
// отбор записей на основе поиска
4471
$query->addUtf($settings->searchExpression)->newLine(); // поиск по словарю
4472
$query->add($settings->lowerBound)->newLine(); // нижняя граница MFN
4473
$query->add($settings->upperBound)->newLine(); // верхняя граница MFN
4474
$query->addUtf($settings->sequentialExpression)->newLine(); // последовательный
4476
// TODO поддержка режима "кроме отмеченных"
4477
if (!$settings->mfnList) {
4478
$count = $settings->maxMfn - $settings->minMfn + 1;
4479
$query->add($count)->newLine();
4480
for ($mfn = $settings->minMfn; $mfn < $settings->maxMfn; $mfn++) {
4481
$query->add($mfn)->newLine();
4484
$query->add(count($settings->mfnList))->newLine();
4485
foreach ($settings->mfnList as $item) {
4486
$query->add($item)->newLine();
4490
if (!$settings->formalControl)
4491
$query->addAnsi('*')->newLine();
4493
if (!$settings->autoin)
4494
$query->addAnsi('&')->newLine();
4496
$response = $this->execute($query);
4497
if (!$response || !$response->checkReturnCode())
4500
return $response->readRemainingAnsiLines();
4501
} // function globalCorrection
4504
* @return bool Получение статуса,
4505
* подключен ли клиент в настоящее время.
4507
public function isConnected()
4509
return $this->connected;
4510
} // function isConnected
4513
* Получение списка баз данных с сервера.
4515
* @param string $specification Спецификация файла со списком баз.
4516
* @return array|bool Список баз данных
4517
* либо признак сбоя операции.
4519
public function listDatabases($specification = '1..dbnam2.mnu')
4521
if (!$this->_checkConnection())
4524
$menu = $this->readMenuFile($specification);
4528
return DatabaseInfo::parseMenu($menu);
4529
} // function listDatabases
4532
* Получение списка файлов.
4534
* @param string $specification Спецификация.
4535
* @return array|bool Список файлов
4536
* либо признак сбоя операции.
4538
public function listFiles($specification)
4540
if (!$this->_checkConnection())
4543
$query = new ClientQuery($this, '!');
4544
$query->addAnsi($specification)->newLine();
4545
$response = $this->execute($query);
4549
$lines = $response->readRemainingAnsiLines();
4551
foreach ($lines as $line) {
4552
$files = irbis_to_lines($line);
4553
foreach ($files as $file) {
4554
if (!is_null_or_empty($file)) {
4561
} // function listFiles
4564
* Получение списка серверных процессов.
4566
* @return array|bool Список процессов
4567
* либо признак сбоя операции.
4569
public function listProcesses()
4571
if (!$this->_checkConnection())
4574
$query = new ClientQuery($this, '+3');
4575
$response = $this->execute($query);
4576
if (!$response || !$response->checkReturnCode())
4579
$lines = $response->readRemainingAnsiLines();
4581
return ProcessInfo::parse($lines);
4582
} // function listProcesses
4585
* Получение списка терминов с указанным префиксом.
4587
* @param string $prefix Префикс.
4588
* @return array Термины (очищенные от префикса)
4589
* (пустой массив при сбое операции).
4591
public function listTerms($prefix)
4595
if (!$this->_checkConnection())
4598
$prefixLength = strlen($prefix);
4599
$startTerm = $prefix;
4600
$lastTerm = $startTerm;
4602
$terms = $this->readTerms($startTerm, 512);
4606
foreach ($terms as $term) {
4607
$text = $term->text;
4608
if (strcmp(substr($text, 0, $prefixLength), $prefix)) {
4611
if ($text !== $startTerm) {
4613
$text = substr($text, $prefixLength);
4617
$startTerm = $lastTerm;
4621
} // function listTerms
4624
* Пустая операция (используется для периодического
4625
* подтверждения подключения клиента).
4627
* @return bool Всегда true при наличии подключения,
4628
* т. к. код возврата не анализируется.
4629
* Всегда false при отсутствии подключения.
4631
public function noOp()
4633
if (!$this->_checkConnection()) {
4637
$query = new ClientQuery($this, 'N');
4638
if (!$this->execute($query))
4645
* Разбор строки подключения.
4647
* @param string $connectionString Строка подключения.
4648
* @throws IrbisException Ошибка в структуре строки подключения.
4650
public function parseConnectionString($connectionString)
4652
$items = explode(';', $connectionString);
4653
foreach ($items as $item) {
4654
if (is_null_or_empty($item)) {
4658
$parts = explode('=', $item, 2);
4659
if (count($parts) !== 2) {
4663
$name = strtolower(trim($parts[0]));
4664
$value = trim($parts[1]);
4670
$this->host = $value;
4674
$this->port = intval($value);
4681
$this->username = $value;
4686
$this->password = $value;
4692
$this->database = $value;
4697
$this->workstation = $value;
4701
$this->debug = $value;
4705
throw new IrbisException("Unknown key $name");
4708
} // function parseConnectionString
4711
* Расформатирование таблицы.
4713
* @param TableDefinition $definition Определение таблицы.
4714
* @return bool|string Результат расформатирования
4715
* либо признак сбоя операции.
4717
public function printTable(TableDefinition $definition)
4719
if (!$this->_checkConnection())
4722
$database = $definition->database ?: $this->database;
4723
$query = new ClientQuery($this, '7');
4724
$query->addAnsi($database)->newLine();
4725
$query->addAnsi($definition->table)->newLine();
4726
$query->addAnsi('')->newLine(); // вместо заголовков
4727
$query->addAnsi($definition->mode)->newLine();
4728
$query->addAnsi($definition->searchQuery)->newLine();
4729
$query->add($definition->minMfn)->newLine();
4730
$query->add($definition->maxMfn)->newLine();
4731
$query->addUtf($definition->sequentialQuery)->newLine();
4732
$query->addAnsi(''); // вместо перечня MFN
4733
$response = $this->execute($query);
4737
return $response->readRemainingUtfText();
4738
} // function printTable
4741
* Получение INI-файла с сервера.
4743
* @param string $specification Спецификация файла.
4744
* @return IniFile|null INI-файл
4745
* либо null в качестве признака сбоя операции.
4747
public function readIniFile($specification)
4749
$lines = $this->readTextLines($specification);
4753
$result = new IniFile();
4754
$result->parse($lines);
4757
} // function readIniFile
4760
* Чтение MNU-файла с сервера.
4762
* @param string $specification Спецификация файла.
4763
* @return bool|MenuFile MNU-файл
4764
* либо признак сбоя операции.
4766
public function readMenuFile($specification)
4768
$lines = $this->readTextLines($specification);
4772
$result = new MenuFile();
4773
$result->parse($lines);
4776
} // function readMenuFile
4779
* Чтение OPT-файла с сервера.
4781
* @param string $specification Спецификация файла.
4782
* @return bool|OptFile OPT-файл
4783
* либо признак сбоя операции.
4784
* @throws IrbisException Ошибка в структуре OPT-файла.
4786
public function readOptFile($specification)
4788
$lines = $this->readTextLines($specification);
4792
$result = new OptFile();
4793
$result->parse($lines);
4796
} // function readOptFile
4799
* Чтение PAR-файла с сервера.
4801
* @param string $specification Спецификация файла.
4802
* @return bool|ParFile PAR-файл
4803
* либо признак сбоя операции.
4804
* @throws IrbisException Ошибка в структуре PAR-файла.
4806
public function readParFile($specification)
4808
$lines = $this->readTextLines($specification);
4812
$result = new ParFile();
4813
$result->parse($lines);
4816
} // function readParFile
4819
* Считывание постингов из поискового индекса.
4821
* @param PostingParameters $parameters Параметры постингов.
4822
* @return array|bool Массив постингов
4823
* либо признак сбоя операции.
4825
public function readPostings(PostingParameters $parameters)
4827
if (!$this->_checkConnection())
4830
$database = $parameters->database ?: $this->database;
4831
$query = new ClientQuery($this, 'I');
4832
$query->addAnsi($database)->newLine();
4833
$query->add($parameters->numberOfPostings)->newLine();
4834
$query->add($parameters->firstPosting)->newLine();
4835
$query->addFormat($parameters->format);
4836
if (!$parameters->listOfTerms) {
4837
$query->addUtf($parameters->term)->newLine();
4839
foreach ($parameters->listOfTerms as $term) {
4840
$query->addUtf($term)->newLine();
4844
$response = $this->execute($query);
4845
if (!$response || !$response->checkReturnCode(codes_for_read_terms()))
4848
$lines = $response->readRemainingUtfLines();
4850
return TermPosting::parse($lines);
4851
} // function readPostings
4854
* Чтение указанной записи в "сыром" виде.
4856
* @param string $mfn MFN записи
4857
* @return bool|RawRecord Запись
4858
* либо признак сбоя операции.
4860
public function readRawRecord($mfn)
4862
if (!$this->_checkConnection())
4865
$query = new ClientQuery($this, 'C');
4866
$query->addAnsi($this->database)->newLine();
4867
$query->add($mfn)->newLine();
4868
$response = $this->execute($query);
4869
if (!$response || !$response->checkReturnCode(codes_for_read_record()))
4872
$result = new RawRecord();
4873
$result->decode($response->readRemainingUtfLines());
4874
$result->database = $this->database;
4877
} // function readRawRecord
4880
* Чтение указанной записи.
4882
* @param int $mfn MFN записи
4883
* @return bool|MarcRecord Запись
4884
* либо признак сбоя операции.
4886
public function readRecord($mfn)
4888
if (!$this->_checkConnection())
4891
$query = new ClientQuery($this, 'C');
4892
$query->addAnsi($this->database)->newLine();
4893
$query->add($mfn)->newLine();
4895
// явно запрашиваем последнюю версию записи,
4896
// т. к. этого требуют сервера от Батрака
4897
$query->add(0)->newLine();
4899
$response = $this->execute($query);
4900
if (!$response || !$response->checkReturnCode(codes_for_read_record()))
4903
$result = new MarcRecord();
4904
$result->decode($response->readRemainingUtfLines());
4905
$result->database = $this->database;
4908
} // function readRecord
4911
* Чтение указанной версии записи.
4913
* @param int $mfn MFN записи
4914
* @param int $version Версия записи
4915
* @return bool|MarcRecord Запись
4916
* либо признак сбоя операции.
4918
public function readRecordVersion($mfn, $version)
4920
if (!$this->_checkConnection())
4923
$query = new ClientQuery($this, 'C');
4924
$query->addAnsi($this->database)->newLine();
4925
$query->add($mfn)->newLine();
4926
$query->add($version);
4928
$response = $this->execute($query);
4929
if (!$response || !$response->checkReturnCode(codes_for_read_record()))
4932
$result = new MarcRecord();
4933
$result->decode($response->readRemainingUtfLines());
4934
$result->database = $this->database;
4937
} // function readRecordVersion
4940
* Чтение с сервера нескольких записей.
4942
* @param array $mfnList Массив MFN.
4943
* @return array Массив записей
4944
* (пустой массив как признак сбоя операции).
4946
public function readRecords(array $mfnList)
4948
if (!$this->_checkConnection())
4955
if (count($mfnList) == 1) {
4957
$record = $this->readRecord($mfnList[0]);
4959
$result[] = $record;
4964
$query = new ClientQuery($this, 'G');
4965
$query->addAnsi($this->database)->newLine();
4966
$query->addAnsi(ALL_FORMAT)->newLine();
4967
$query->add(count($mfnList))->newLine();
4968
foreach ($mfnList as $mfn) {
4969
$query->add($mfn)->newLine();
4971
$response = $this->execute($query);
4972
if (!$response || !$response->checkReturnCode())
4975
$lines = $response->readRemainingUtfLines();
4977
foreach ($lines as $line) {
4978
$parts = explode('#', $line, 2);
4979
if (count($parts) > 1) {
4980
$parts = explode("\x1F", $parts[1]);
4981
$parts = array_slice($parts, 1);
4982
$record = new MarcRecord();
4983
$record->decode($parts);
4984
$record->database = $this->database;
4985
$result[] = $record;
4990
} // function readRecords
4993
* Загрузка сценариев поиска с сервера.
4995
* @param string $specification Спецификация.
4996
* @return array|bool Массив сценариев
4997
* либо признак сбоя операции.
4999
public function readSearchScenario($specification)
5001
if (!$this->_checkConnection())
5004
$iniFile = $this->readIniFile($specification);
5008
return SearchScenario::parse($iniFile);
5009
} // function readSearchScenario
5012
* Простое получение терминов поискового словаря.
5014
* @param string $startTerm Начальный термин.
5015
* @param int $numberOfTerms Необходимое количество терминов.
5016
* @return array|bool Массив терминов
5017
* либо призак сбоя операции.
5019
public function readTerms($startTerm, $numberOfTerms = 100)
5021
$parameters = new TermParameters();
5022
$parameters->startTerm = $startTerm;
5023
$parameters->numberOfTerms = $numberOfTerms;
5025
return $this->readTermsEx($parameters);
5026
} // function readTerms
5029
* Получение терминов поискового словаря.
5031
* @param TermParameters $parameters Параметры терминов.
5032
* @return array|bool Массив терминов
5033
* либо признак сбоя операции.
5035
public function readTermsEx(TermParameters $parameters)
5037
if (!$this->_checkConnection())
5040
$command = $parameters->reverseOrder ? 'P' : 'H';
5041
$database = $parameters->database ?: $this->database;
5042
$query = new ClientQuery($this, $command);
5043
$query->addAnsi($database)->newLine();
5044
$query->addUtf($parameters->startTerm)->newLine();
5045
$query->add($parameters->numberOfTerms)->newLine();
5046
$query->addFormat($parameters->format);
5047
$response = $this->execute($query);
5048
if (!$response || !$response->checkReturnCode(codes_for_read_terms()))
5051
$lines = $response->readRemainingUtfLines();
5053
return TermInfo::parse($lines);
5054
} // function readTermsEx
5057
* Получение текстового файла с сервера.
5059
* @param string $specification Спецификация файла.
5060
* @return bool|string Текст файла
5061
* либо признак сбоя операции.
5063
public function readTextFile($specification)
5065
if (!$this->_checkConnection())
5068
$query = new ClientQuery($this, 'L');
5069
$query->addAnsi($specification)->newLine();
5070
$response = $this->execute($query);
5074
$result = $response->readAnsi();
5075
$result = irbis_to_dos($result);
5078
} // function readTextFile
5081
* Получение текстового файла в виде массива строк.
5083
* @param string $specification Спецификация файла.
5084
* @return array Массив строк
5085
* (пустой массив как признак сбоя операции).
5087
public function readTextLines($specification)
5089
if (!$this->_checkConnection())
5092
$query = new ClientQuery($this, 'L');
5093
$query->addAnsi($specification)->newLine();
5094
$response = $this->execute($query);
5098
$result = $response->readAnsi();
5099
$result = irbis_to_lines($result);
5102
} // function readTextLines
5105
* Чтение TRE-файла с сервера.
5107
* @param string $specification Спецификация файла.
5108
* @return bool|TreeFile TRE-файл
5109
* либо признак сбоя операции.
5110
* @throws IrbisException Ошибка в структуре TRE-файла.
5112
public function readTreeFile($specification)
5114
$lines = $this->readTextLines($specification);
5118
$result = new TreeFile();
5119
$result->parse($lines);
5122
} // function readTreeFile
5125
* Пересоздание словаря для указанной базы данных.
5127
* @param string $database База данных.
5128
* @return bool Признак успешности операции.
5130
public function reloadDictionary($database)
5132
if (!$this->_checkConnection())
5135
$query = new ClientQuery($this, 'Y');
5136
$query->addAnsi($database)->newLine();
5137
if (!$this->execute($query))
5141
} // function reloadDictionary
5144
* Пересоздание мастер-файла для указанной базы данных.
5146
* @param string $database База данных.
5147
* @return bool Признак успешности операции.
5149
public function reloadMasterFile($database)
5151
if (!$this->_checkConnection())
5154
$query = new ClientQuery($this, 'X');
5155
$query->addAnsi($database)->newLine();
5156
if (!$this->execute($query))
5160
} // function reloadMasterFile
5163
* Получение INI-файла с сервера.
5165
* @param string $specification Спецификация файла.
5166
* @return IniFile Полученный INI-файл.
5167
* @throws IrbisException Файл не найден.
5169
public function requireIniFile($specification)
5171
$lines = $this->readTextLines($specification);
5173
throw new IrbisException("File not found: " . $specification);
5175
$result = new IniFile();
5176
$result->parse($lines);
5179
} // function requireIniFile
5182
* Получение MNU-файла с сервера.
5184
* @param string $specification Спецификация файла.
5185
* @return MenuFile Полученный MNU-файл.
5186
* @throws IrbisException Файл не найден.
5188
public function requireMenuFile($specification)
5190
$lines = $this->readTextLines($specification);
5192
throw new IrbisException("File not found: " . $specification);
5194
$result = new MenuFile();
5195
$result->parse($lines);
5198
} // function requireMenuFile
5201
* Получение OPT-файла с сервера.
5203
* @param string $specification Спецификация файла.
5204
* @return OptFile Полученный OPT-файл.
5205
* @throws IrbisException Файл не найден.
5207
public function requireOptFile($specification)
5209
$lines = $this->readTextLines($specification);
5211
throw new IrbisException("File not found: " . $specification);
5213
$result = new OptFile();
5214
$result->parse($lines);
5217
} // function requireOptFile
5220
* Получение PAR-файла с сервера.
5222
* @param string $specification Спецификация файла.
5223
* @return ParFile Полученный PAR-файл.
5224
* @throws IrbisException Файл не найден.
5226
public function requireParFile($specification)
5228
$lines = $this->readTextLines($specification);
5230
throw new IrbisException("File not found: " . $specification);
5232
$result = new ParFile();
5233
$result->parse($lines);
5236
} // function requireParFile
5239
* Получение текстового файла с сервера.
5241
* @param string $specification Спецификация файла.
5242
* @return string Текст полученного файла.
5243
* @throws IrbisException Файл не найден.
5245
public function requireTextFile($specification)
5247
$result = $this->readTextFile($specification);
5248
if (!$result || is_null_or_empty($result))
5249
throw new IrbisException("File not found: " . $specification);
5252
} // function requireTextFile
5255
* Получение TRE-файла с сервера.
5257
* @param string $specification Спецификация файла.
5258
* @return TreeFile Полученный TRE-файл.
5259
* @throws IrbisException Файл не найден.
5261
public function requireTreeFile($specification)
5263
$lines = $this->readTextLines($specification);
5265
throw new IrbisException("File not found: " . $specification);
5267
$result = new TreeFile();
5268
$result->parse($lines);
5271
} // function requireTreeFile
5274
* Перезапуск сервера (без утери подключенных клиентов).
5276
* @return bool Признак успешности операции.
5278
public function restartServer()
5280
if (!$this->_checkConnection())
5283
$query = new ClientQuery($this, '+8');
5284
if (!$this->execute($query))
5288
} // function restartServer
5291
* Простой поиск записей (не более 32 тыс. записей).
5293
* @param string $expression Выражение для поиска по словарю.
5294
* @return array|bool Массив найденных MFN
5295
* либо признак сбоя операции.
5297
public function search($expression)
5299
$parameters = new SearchParameters();
5300
$parameters->expression = $expression;
5301
$found = $this->searchEx($parameters);
5303
return FoundLine::toMfn($found);
5304
} // function search
5307
* Поиск всех записей (даже если их окажется больше 32 тыс.).
5309
* @param string $expression Выражение для поиска по словарю.
5310
* @return array Массив MFN найденных записей
5311
* (возможно, пустой).
5313
public function searchAll($expression)
5316
if (!$this->_checkConnection())
5323
$query = new ClientQuery($this, 'K');
5324
$query->addAnsi($this->database)->newLine();
5325
$query->addUtf((string)$expression)->newLine();
5326
$query->add(0)->newLine();
5327
$query->add($firstRecord)->newLine();
5328
$response = $this->execute($query);
5329
if (!$response || !$response->checkReturnCode())
5330
return $result; // TODO реагировать правильно
5332
if ($firstRecord == 1) {
5333
$totalCount = $response->readInteger();
5338
$response->readInteger(); // Eat the line
5341
$lines = $response->readRemainingUtfLines();
5342
$found = FoundLine::parseMfn($lines);
5346
$result = $result + $found;
5347
$firstRecord += count($found);
5348
if ($firstRecord >= $totalCount)
5353
} // function searchAll
5356
* Определение количества записей,
5357
* соответствующих поисковому выражению.
5359
* @param string $expression Поисковое выражение.
5360
* @return int Количество соответствующих записей.
5362
public function searchCount($expression)
5364
if (!$this->_checkConnection())
5367
$query = new ClientQuery($this, 'K');
5368
$query->addAnsi($this->database)->newLine();
5369
$query->addUtf((string)$expression)->newLine();
5370
$query->add(0)->newLine();
5372
$response = $this->execute($query);
5373
if (!$response || !$response->checkReturnCode())
5376
return $response->readInteger(); // Число найденных записей
5377
} // function searchCount
5380
* Расширенный поиск записей.
5382
* @param SearchParameters $parameters Параметры поиска.
5383
* @return array|bool Массив найденных записей
5384
* либо признак сбоя операции.
5386
public function searchEx(SearchParameters $parameters)
5388
if (!$this->_checkConnection())
5391
$database = $parameters->database ?: $this->database;
5392
$query = new ClientQuery($this, 'K');
5393
$query->addAnsi($database)->newLine();
5394
$query->addUtf((string)($parameters->expression))->newLine();
5395
$query->add($parameters->numberOfRecords)->newLine();
5396
$query->add($parameters->firstRecord)->newLine();
5397
$query->addFormat($parameters->format);
5398
$query->add($parameters->minMfn)->newLine();
5399
$query->add($parameters->maxMfn)->newLine();
5400
$query->addAnsi($parameters->sequential)->newLine();
5401
$response = $this->execute($query);
5402
if (!$response || !$response->checkReturnCode())
5405
$response->readInteger(); // Число найденных записей.
5406
$lines = $response->readRemainingUtfLines();
5407
$result = FoundLine::parse($lines);
5410
} // function searchEx
5413
* Поиск записей с их одновременным считыванием.
5415
* @param string $expression Поисковое выражение.
5416
* @param int $limit Максимальное количество загружаемых записей.
5417
* @return array Массив полученных записей
5418
* (возможно, пустой).
5420
public function searchRead($expression, $limit = 0)
5422
$parameters = new SearchParameters();
5423
$parameters->expression = $expression;
5424
$parameters->format = ALL_FORMAT;
5425
$parameters->numberOfRecords = $limit;
5426
$found = $this->searchEx($parameters);
5431
foreach ($found as $item) {
5432
$lines = explode("\x1F", $item->description);
5433
$lines = array_slice($lines, 1);
5434
$record = new MarcRecord();
5435
$record->decode($lines);
5436
$record->database = $this->database;
5437
$result[] = $record;
5441
} // function searchRead
5444
* Поиск и считывание одной записи, соответствующей выражению.
5445
* Если таких записей больше одной, то будет считана любая из них.
5446
* Если таких записей нет, будет возвращен null.
5448
* @param string $expression Поисковое выражение.
5449
* @return MarcRecord|null Полученная запись либо null,
5450
* если запись не найдена.
5452
public function searchSingleRecord($expression)
5454
$found = $this->searchRead($expression, 1);
5459
} // function searchSingleRecord
5462
* Бросает исключение, если произошла ошибка
5463
* при выполнении последней операции.
5464
* @throws IrbisException Обнаружена ошибка,
5465
* выброшено исключение.
5467
public function throwOnError()
5469
if ($this->lastError < 0)
5470
throw new IrbisException($this->lastError);
5471
} // function throwOnError
5474
* Выдача строки подключения для текущего соединения.
5475
* Соединение не обязательно должно быть установлено.
5477
* @return string Строка подключения для текушего соединения
5478
* (не обязательно активного).
5480
public function toConnectionString()
5482
return 'host=' . $this->host
5483
. ';port=' . $this->port
5484
. ';username=' . $this->username
5485
. ';password=' . $this->password
5486
. ';database=' . $this->database
5487
. ';arm=' . $this->workstation . ';';
5488
} // function toConnectionString
5491
* Опустошение указанной базы данных.
5493
* @param string $database База данных.
5494
* @return bool Признак успешности операции.
5496
public function truncateDatabase($database)
5498
if (!$this->_checkConnection()) {
5502
$query = new ClientQuery($this, 'S');
5503
$query->addAnsi($database)->newLine();
5504
if (!$this->execute($query))
5508
} // function truncateDatabase
5511
* Восстановление записи по её MFN.
5513
* @param int $mfn MFN восстанавливаемой записи.
5514
* @return bool|MarcRecord Восстановленная запись
5515
* либо признак сбоя операции.
5517
public function undeleteRecord($mfn)
5519
$record = $this->readRecord($mfn);
5523
if ($record->isDeleted()) {
5524
$record->status &= ~LOGICALLY_DELETED;
5525
if (!$this->writeRecord($record))
5530
} // function undeleteRecord
5533
* Разблокирование указанной базы данных.
5535
* @param string $database База данных.
5536
* @return bool Признак успешности операции.
5538
public function unlockDatabase($database)
5540
if (!$this->_checkConnection())
5543
$query = new ClientQuery($this, 'U');
5544
$query->addAnsi($database)->newLine();
5545
if (!$this->execute($query))
5549
} // function unlockDatabase
5552
* Разблокирование записей.
5554
* @param string $database База данных.
5555
* @param array $mfnList Массив MFN.
5556
* @return bool Признак успешности операции.
5558
public function unlockRecords($database, array $mfnList)
5560
if (!$this->_checkConnection())
5563
if (count($mfnList) == 0)
5566
$database = $database ?: $this->database;
5567
$query = new ClientQuery($this, 'Q');
5568
$query->addAnsi($database)->newLine();
5569
foreach ($mfnList as $mfn)
5570
$query->add($mfn)->newLine();
5572
if (!$this->execute($query))
5576
} // function unlockRecords
5579
* Обновление строк серверного INI-файла
5580
* для текущего пользователя.
5582
* @param array $lines Изменённые строки.
5583
* @return bool Признак успешности операции.
5585
public function updateIniFile(array $lines)
5587
if (!$this->_checkConnection())
5593
$query = new ClientQuery($this, '8');
5594
foreach ($lines as $line)
5595
$query->addAnsi($line)->newLine();
5597
if (!$this->execute($query))
5601
} // function updateIniFile
5604
* Обновление списка пользователей на сервере.
5606
* @param array $users Список пользователей.
5607
* @return bool Признак успешности операции.
5609
public function updateUserList(array $users)
5611
if (!$this->_checkConnection())
5614
$query = new ClientQuery($this, '+7');
5615
foreach ($users as $user)
5616
$query->addAnsi($user->encode())->newLine();
5617
if (!$this->execute($query))
5621
} // function updateUserList
5624
* Сохранение на сервере "сырой" записи.
5626
* @param RawRecord $record Запись для сохранения.
5627
* @return bool|int Новый максимальный MFN в базе данных
5628
* либо признак сбоя операции.
5630
public function writeRawRecord(RawRecord $record)
5632
if (!$this->_checkConnection())
5635
$database = $record->database ?: $this->database;
5636
$query = new ClientQuery($this, 'D');
5637
$query->addAnsi($database)->newLine();
5638
$query->add(0)->newLine();
5639
$query->add(1)->newLine();
5640
$query->addUtf($record->encode())->newLine();
5641
$response = $this->execute($query);
5642
if (!$response || !$response->checkReturnCode())
5645
return $response->returnCode;
5646
} // function writeRawRecord
5649
* Сохранение записи на сервере.
5651
* @param MarcRecord $record Запись для сохранения (новая или ранее считанная).
5652
* @param int $lockFlag Оставить запись заблокированной?
5653
* @param int $actualize Актуализировать словарь?
5654
* @param bool $dontParse Не разбирать результат.
5655
* @return bool|int Новый максимальный MFN в базе данных
5656
* либо признак сбоя операции.
5658
public function writeRecord(MarcRecord $record, $lockFlag = 0, $actualize = 1,
5661
if (!$this->_checkConnection())
5664
$database = $record->database ?: $this->database;
5665
$query = new ClientQuery($this, 'D');
5666
$query->addAnsi($database)->newLine();
5667
$query->add($lockFlag)->newLine();
5668
$query->add($actualize)->newLine();
5669
$query->addUtf($record->encode())->newLine();
5670
$response = $this->execute($query);
5671
if (!$response || !$response->checkReturnCode())
5675
$record->fields = array();
5676
$temp = $response->readRemainingUtfLines();
5677
if (count($temp) > 1) {
5678
$lines = array($temp[0]);
5679
$lines = array_merge($lines, explode(SHORT_DELIMITER, $temp[1]));
5680
$record->decode($lines);
5681
$record->database = $database;
5685
return $response->returnCode;
5686
} // function writeRecord
5689
* Сохранение нескольких записей на сервере (могут относиться к разным базам).
5691
* @param array $records Записи.
5692
* @param int $lockFlag
5693
* @param int $actualize
5694
* @param bool $dontParse
5695
* @return bool Признак успешности операции.
5697
public function writeRecords(array $records, $lockFlag = 0, $actualize = 1,
5700
if (!$this->_checkConnection())
5706
if (count($records) == 1) {
5707
$this->writeRecord($records[0]);
5712
$query = new ClientQuery($this, '6');
5713
$query->add($lockFlag)->newLine();
5714
$query->add($actualize)->newLine();
5715
foreach ($records as $record) {
5716
$database = $record->database ?: $this->database;
5717
$query->addUtf($database . IRBIS_DELIMITER . $record->encode())->newLine();
5720
$response = $this->execute($query);
5724
$response->getReturnCode();
5727
$lines = $response->readRemainingUtfLines();
5728
$length = count($records);
5729
for ($i = 0; $i < $length; $i++) {
5731
if (is_null_or_empty($text)) {
5735
$record = $records[$i];
5737
$record->database = $record->database ?: $this->database;
5738
$recordLines = irbis_to_lines($text);
5739
$record->parse($recordLines);
5744
} // function writeRecords
5747
* Сохранение текстового файла на сервере.
5749
* @param string $specification Спецификация файла
5750
* (включая текст файла).
5751
* @return bool Признак успешности операции.
5753
public function writeTextFile($specification)
5755
if (!$this->_checkConnection())
5758
$query = new ClientQuery($this, 'L');
5759
$query->addAnsi($specification);
5760
if (!$this->execute($query))
5764
} // function writeTextFile
5766
} // class Connection
5772
* @var Connection Активное подключение к серверу.
5779
* @param Connection $connection Активное (!) подключение к серверу.
5780
* @throws IrbisException
5782
public function __construct(Connection $connection)
5784
if (!$connection->isConnected())
5785
throw new IrbisException();
5787
$this->connection = $connection;
5791
* Вывод выпадающего списка баз данных.
5793
* @param string $class
5794
* @param string $selected
5795
* @throws IrbisException
5797
public function listDatabases($class = '', $selected = '')
5799
$dbnnamecat = $this->connection->iniFile->getValue('Main', 'DBNNAMECAT', 'dbnam3.mnu');
5800
$databases = $this->connection->listDatabases('1..' . $dbnnamecat);
5802
throw new IrbisException();
5806
$classText = "class='{$class}'";
5808
echo "<select name='catalogBox' $classText>" . PHP_EOL;
5809
foreach ($databases as $database) {
5811
if (same_string($database->name, $selected)) {
5812
$selectedText = 'selected';
5814
echo "<option value='{$database->name}' $selectedText>{$database->description}</option>" . PHP_EOL;
5816
echo "</select>" . PHP_EOL;
5817
} // function listDatabases
5820
* Получение сценариев поиска.
5823
* @throws IrbisException
5825
public function getSearchScenario()
5828
$ini = $this->connection->iniFile;
5829
$fileName = $ini->getValue("MAIN", 'SearchIni'); // ???
5830
$section = $ini->findSection("SEARCH");
5832
throw new IrbisException();
5834
$result = SearchScenario::parse($ini);
5837
} // function getSearchScenario
5840
* Вывод выпадающего списка сценариев поиска.
5844
* @param string $class
5845
* @param int $selectedIndex
5846
* @param string $selectedValue
5848
public function listSearchScenario($name, $scenarios, $class = '', $selectedIndex = -1,
5849
$selectedValue = '')
5851
echo "<select name='$name'>" . PHP_EOL;
5854
$classText = " class='$class'";
5857
foreach ($scenarios as $scenario) {
5859
if ($selectedValue) {
5860
if (same_string($scenario->prefix, $selectedValue)) {
5861
$selectedText = 'selected';
5863
} else if ($index == $selectedIndex) {
5864
$selectedText = 'selected';
5866
echo "<option value='{$scenario->prefix}' $selectedText $classText>{$scenario->name}</option>" . PHP_EOL;
5869
echo "</select>" . PHP_EOL;
5870
} // function listSearchScenario
5875
* Запись в XRF-файле. Содержит информацию о смещении записи
5878
final class XrfRecord
5881
* @var int Младшая часть смещения.
5886
* @var int Старшая часть смещения.
5891
* @var int Статус записи.
5896
* Запись (логически или физически) удалена?
5899
public function isDeleted()
5901
return ($this->status & 3) != 0;
5908
public function offset()
5910
return ($this->high << 32) + $this->low;
5923
* XrfFile constructor.
5925
* @throws IrbisException
5927
public function __construct($filename)
5929
$this->file = fopen($filename, 'rb');
5931
throw new IrbisException("Can't open " . $filename);
5933
} // function __construct
5935
public function __destruct()
5938
fclose($this->file);
5939
} // function __destruct
5942
* Считывание записи по MFN.
5943
* @param int $mfn MFN записи.
5946
public function read($mfn)
5948
$offset = ($mfn - 1) * 12;
5949
fseek($this->file, $offset, SEEK_SET);
5950
$content = fread($this->file, 12);
5951
$result = new XrfRecord();
5952
$result->low = unpack("N", $content, 0);
5953
$result->high = unpack("N", $content, 4);
5954
$result->status = unpack("N", $content, 8);