1: <?php
2: require_once 'postcalc_light_config.php';
3: /**
4: * Основная функция опроса сервера Postcalc.RU
5: *
6: * Настройки хранятся в конфигурационном файле файле postcalc_light_config.php.<br>
7: *
8: * Принимает следующие данные: отправитель, получатель, вес, оценка, страна. <br>
9: *
10: * 1). Проверяет эти данные, при ошибке возвращает строку с сообщением об ошибке.<br>
11: *
12: * 2). В цикле опрашивает сервера проекта Postcalc.RU (переменная servers
13: * конфигурационного файла).<br>
14: *
15: * 3). В случае успеха возвращает массив с полученными от сервера данными,
16: * при ошибке - строку с сообщением об ошибке.<br>
17: *
18: * 4). Использует кэширование: в случае успеха записывает ответ в каталог cache_dir,
19: * хранит ответ в течение cache_valid секунд. <br>
20: *
21: * <code>
22: * $Response=postcalc_request('101000', 'Александровка, Алтайский край, Локтевский район', 505.1, 1000, 'RU');
23: *
24: * if (is_array($Response)) {
25: * echo $Response['Отправления']['ПростаяБандероль']['Тариф'];
26: * } else {
27: * echo "Ошибка: $Response";
28: * }
29: * </code>
30: *
31: * @uses postcalc_get_default_ops() Используется при валидации отправителя и получателя.
32: * @uses postcalc_arr_from_txt() Используется при валидации страны.
33: *
34: * @param string $From Отправитель. Либо 6-значный индекс ОПС, который проверяется по файлу postcalc_light_post_indexes.txt
35: * или таблице postcalc_light_post_indexes, либо наименование населенного пункта, которое проверяется по файлу
36: * postcalc_light_cities.txt или таблице postcalc_light_cities.
37: *
38: * @param string $To Получатель. Либо 6-значный индекс ОПС, который проверяется по файлу postcalc_light_post_indexes.txt
39: * или таблице postcalc_light_post_indexes, либо наименование населенного пункта, которое проверяется по файлу
40: * postcalc_light_cities.txt или таблице postcalc_light_cities.
41: *
42: * @param float $Weight Вес в граммах, от 1 до 100000.
43: *
44: * @param float $Valuation Оценка почтового отправления в рублях, от 0 до 100000.
45: *
46: * @param string $Country Двухбуквенный код страны, проверяется по файлу postcalc_light_countries.txt или
47: * таблице postcalc_light_countries. Если отличается от RU, поле $To игнорируется.
48: *
49: * @return array|string В случае успеха возвращает массив с данными, полученными от сервера Postcalc.RU.
50: * При ошибке возвращает строку с сообщением об ошибке.
51: *
52: * @since 10.05.2014
53: *
54: * @author Postcalc.RU <postcalc@mail.ru>
55: *
56: * @version 2.0
57: *
58: *
59: *
60: */
61: function postcalc_request($From, $To, $Weight, $Valuation=0, $Country='RU')
62: {
63: global $arrPostcalcConfig;
64: extract($arrPostcalcConfig, EXTR_PREFIX_ALL, 'config');
65: // Обязательно! Проверяем данные - больше всего ошибочных запросов из-за неверных значений веса и оценки,
66: // из-за пропущенного поля "Куда".
67: if ( !is_numeric($Weight) || !($Weight > 0 && $Weight <= 100000) )
68: return "Bec в граммах - число от 1 до 100000, десятичный знак - точка!";
69: if ( !is_numeric($Valuation) || !($Valuation >= 0 && $Valuation <= 100000) )
70: return "Оценка в рублях - число от 0 до 100000, десятичный знак - точка!";
71:
72: // Отдельная функция проверяет правильность полей Откуда и Куда
73: //$From = mb_convert_case($From, MB_CASE_TITLE, $config_cs);
74: $PindexFrom = postcalc_get_default_ops($From);
75: if ( !$PindexFrom )
76: return "Поле 'Откуда': '$From' - не является допустимым индексом, названием региона или центра региона!";
77:
78:
79: //$To = mb_convert_case($To, MB_CASE_TITLE, $config_cs);
80: $PindexTo = postcalc_get_default_ops($To);
81: if ( !$PindexTo )
82: return "Поле 'Куда': '$To' - не является допустимым индексом, названием региона или центра региона!";
83: // Если установлен флаг $config_city_as_pindex, то в запрос подставляем почтовый индекс по умолчанию.
84: // Если флаг $config_city_as_pindex не установлен, переводим название нас.пункта в "процентную" кодировку.
85: $From = ( $config_city_as_pindex ) ? $PindexFrom : rawurlencode($From);
86: $To = ( $config_city_as_pindex ) ? $PindexTo : rawurlencode($To);
87:
88: $Country = mb_convert_case($Country, MB_CASE_TITLE, $config_cs);
89: if ( !postcalc_arr_from_txt('postcalc_light_countries.txt', $Country, 1) )
90: return "Код страны '$Country' не найден в базе стран postcalc_light_countries.txt!";
91:
92: // Формируем запрос со всеми необходимыми переменными.
93: $QueryString = "st=$config_st&ml=$config_ml";
94: $QueryString .= "&f=$From&t=$To&w=$Weight&v=$Valuation&c=$Country";
95: $QueryString .= "&o=php&sw=PostcalcLight_2.0&cs=$config_cs";
96: if ( $config_d != 'now' ) $QueryString .= "&d=$config_d";
97: if ( $config_ib != 'f' ) $QueryString .= "&ib=$config_ib";
98: if ( $config_r != 0.01 ) $QueryString .= "&r=$config_r";
99: if ( $config_pr > 0 ) $QueryString .= "&pr=$config_pr";
100: if ( $config_pk > 0 ) $QueryString .= "&pk=$config_pk";
101:
102: // Название файла - префикс postcalc_ плюс хэш строки запроса
103: $CacheFile = "$config_cache_dir/postcalc_".md5($QueryString).'.txt';
104: // Сборка мусора. Удаляем все файлы, которые подходят под маску, старше POSTCALC_CACHE_VALID секунд
105: $arrCacheFiles = glob( "$config_cache_dir/postcalc_*.txt" );
106: $Now = time();
107: foreach ( $arrCacheFiles as $fileObj )
108: if ( $Now-filemtime($fileObj) > $config_cache_valid ) unlink( $fileObj );
109:
110: // Если существует файл кэша для данной строки запроса, просто зачитываем его
111: if ( file_exists($CacheFile) ) {
112: return unserialize( file_get_contents($CacheFile) );
113: } else {
114: // Формируем опции запроса. Это _необязательно_, однако упрощает контроль и отладку
115: $arrOptions = array('http' =>
116: array( 'header' => 'Accept-Encoding: gzip',
117: 'timeout' => $config_timeout,
118: 'user_agent' => 'PostcalcLight_2.0 '.phpversion()
119: )
120: );
121: $TS=microtime(1);
122: // Опрашиваем в цикле сервера Postcalc.RU, пока не получаем ответ
123: $ConnectOK=0;
124: foreach ( $config_servers as $Server ) {
125: // Запрос к серверу. Сохраняем ответ в переменной $Response.
126: // При ошибке соединения опрашиваем следующий сервер в цепочке.
127: if ( !$Response = file_get_contents("http://$Server/?$QueryString", false , stream_context_create($arrOptions)) ) {
128: // === ОБРАБОТКА ОШИБОК СОЕДИНЕНИЯ
129: // Журнал ошибок соединения, поля разделены табуляцией:
130: // метка времени, сервер, истекшее время с начала сессии (т.е. всех запросов), краткое сообщение об ошибке, полное сообщение об ошибке
131: if ( $config_error_log && count(error_get_last()) ) {
132: $ErrorLog = "$config_cache_dir/postcalc_error_" .date('Y-m') .'.log';
133: $arrError = error_get_last();
134: $PHPErrorMessage = $arrError['message'];
135: // Отрезаем конец сообщения PHP, где сообщается причина проблемы
136: $ErrMessage = substr( $PHPErrorMessage, strrpos( $PHPErrorMessage,':')+2 );
137: $fp_log = fopen($ErrorLog,'a');
138: fwrite($fp_log, date('Y-m-d H:i:s') ."\t$Server\t" .number_format((microtime(1)-$TS),3) ."\t$ErrMessage\t$PHPErrorMessage\n");
139: fclose($fp_log);
140: if ( $config_error_log_send > 0 ) {
141: $fp_log = fopen($ErrorLog,'r');
142: // Последовательно идем по логу и сохраняем в переменной $MailMessage фрагмент не более $config_error_log_send строк
143: $MailMessage = ''; $send_log=false; $line_counter = 0;
144: while ( ($line = fgets($fp_log)) !== false) {
145: $line_counter++;
146: if ( $send_log ) {
147: $MailMessage = '';
148: $send_log=false;
149: }
150: $MailMessage .= $line;
151: // Если в $MailMessage оказалось ровно $config_error_log_send строк, сбрасываем счетчик строк и устанавливаем флаг $send_log.
152: // Если следующее чтение вернуло конец файла, цикл будет прерван и фрагмент лога отослан по почте.
153: // Иначе фрагмент лога будет сброшен, как и флаг $send_log
154: if ( $line_counter % $config_error_log_send === 0 ) {
155: $line_counter = 0;
156: $send_log=true;
157: }
158: }
159: fclose($fp_log);
160: if ( $send_log ) {
161: $MailMessage="$_SERVER[SERVER_ADDR] [$_SERVER[SERVER_ADDR]]: ошибки соединения в скрипте $_SERVER[SCRIPT_FILENAME].\n"
162: . "Подробности см. в http://$_SERVER[HTTP_HOST]".dirname($_SERVER['REQUEST_URI'])."/postcalc_light_stat.php\n"
163: . "Последние строки ($config_error_log_send) из журнала ошибок:\n\n"
164: . $MailMessage;
165: mail($config_ml,
166: "$_SERVER[SERVER_ADDR] [$_SERVER[SERVER_ADDR]]: connection errors in postcalc_light_lib",
167: $MailMessage,
168: "Content-Transfer-Encoding: 8bit\nContent-Type: text/plain; charset=$config_cs\n");
169: }
170: }
171: }
172: // === КОНЕЦ ОБРАБОТКИ ОШИБОК СОЕДИНЕНИЯ
173: continue;
174: }
175: $ConnectOK = 1;
176: break;
177: }
178: if ( !$ConnectOK ) return 'Не удалось соединиться ни с одним из следующих серверов postcalc.ru: '.implode(',',$config_servers).'. Проверьте соединение с Интернетом.';
179:
180: $ResponseSize = strlen( $Response );
181:
182: // Если поток сжат, разархивируем его
183: if ( substr($Response, 0, 3) == "\x1f\x8b\x08" )
184: $Response = gzinflate( substr($Response, 10, -8) );
185:
186: // Переводим ответ сервера в массив PHP
187: if ( !$arrResponse = unserialize($Response) )
188: return "Получены странные данные. Ответ сервера:\n$Response";
189:
190: // Обработка возможной ошибки
191: if ( $arrResponse['Status'] != 'OK' )
192: return "Сервер вернул ошибку: $arrResponse[Status]!";
193:
194: // Журнал успешных соединений, поля разделены табуляцией:
195: // метка времени, сервер, затраченное время, размер ответа, строка запроса
196: if ( $config_log ) {
197: $fp_log = fopen("$config_cache_dir/postcalc_light_" .date('Y-m') .'.log','a');
198: fwrite($fp_log,date('Y-m-d H:i:s')."\t$Server\t" .number_format((microtime(1)-$TS),3) ."\t$ResponseSize\t$QueryString\n");
199: fclose($fp_log);
200: }
201: // Успешный ответ пишем в кэш
202: file_put_contents($CacheFile, $Response);
203:
204: return $arrResponse;
205: }
206:
207: }
208:
209: /**
210: * Функция проверки правильности отправителя или получателя. Принимает либо 6-значный индекс,
211: * либо название населенного пункта из файла postcalc_light_cities.txt или таблицы postcalc_light_cities.
212: * Например: 'Москва', 'Абагур (Новокузнецк)', 'Абрамцево, Московская область, Сергиево-Посадский район'.
213: *
214: * Возвращает 6-значный индекс ОПС, если не найдено - false.
215: *
216: * Если передан 6-значный индекс, проверка идет по текстовому файлу postcalc_light_post_indexes.txt
217: * или таблице postcalc_light_post_indexes, где находятся все почтовые индексы России в формате
218: * индекс ОПС - название ОПС из "эталонного справочника Почты России".
219: *
220: * <code>
221: * $From = 'Сергиев Посад';
222: *
223: * $postIndex = postcalc_get_default_ops($From);
224: *
225: * if ( !$postIndex ) echo "'$From' не является допустимым индексом, названием региона или центра региона!";
226: * </code>
227: *
228: * @param string $FromTo Проверяемое значение
229: * @return string При ошибке возвращает false, иначе - шестизначный индекс ОПС.
230: *
231: * @uses postcalc_arr_from_txt() Запрашивает массив, созданный из текстового файла.
232: */
233: function postcalc_get_default_ops( $FromTo )
234: {
235: if ( !$FromTo ) return false;
236:
237: if ( preg_match('/^[1-9][0-9]{5}$/',$FromTo) ) {
238: // Это 6-значный индекс.
239: $isPindex = true;
240: $arr = postcalc_arr_from_txt('postcalc_light_post_indexes.txt', $FromTo);
241: } else {
242: // Это любое другое сочетание букв и цифр
243: $isPindex = false;
244: $arr = postcalc_arr_from_txt('postcalc_light_cities.txt', $FromTo);
245: }
246: // Ищем точное совпадение $FromTo и ключа в массиве.
247: if ( isset($arr[$FromTo]) )
248: return ($isPindex) ? $FromTo : $arr[$FromTo];
249:
250: return false;
251: }
252: /**
253: * Функция генерирует массив PHP либо из текстового файла с данными,
254: * либо из таблицы MySQL.
255: *
256: * В первом случае открывает файл $src_txt, в котором находятся данные в формате:
257: * [ключ]\t[значение]\n.
258: *
259: * Во втором случае обращается к таблице MySQL. Ее название совпадает с названием
260: * текстового файла без расширения .txt.
261: *
262: * Возвращает массив. Параметр search - совпадение с началом ключа. Если пустая
263: * строка (по умолчанию) - возвращает все строки.
264: *
265: * @param string $src_txt Название файла с данными (включая расширение .txt).
266: * @param string $search Совпадение с началом ключа. Если пустая строка - возвращает полную таблицу.
267: * @param integer $limit Возвращать не более $limit элементов (для Autocomplete)
268: * @return array Массив, если совпадений нет - пустой массив
269: *
270: */
271: function postcalc_arr_from_txt($src_txt, $search = '', $limit = 0){
272: global $arrPostcalcConfig;
273: extract($arrPostcalcConfig, EXTR_PREFIX_ALL, 'config');
274: $arr=array();
275: // === Источник - таблица mysql
276: if ( $config_source == 'mysql' ) {
277: $mysql = new MySQLi($config_mysql_host, $config_mysql_user, $config_mysql_pass, $config_mysql_db);
278: $TableName = basename($src_txt, '.txt');
279: // Небольшой хак, чтобы установить имя ключевого поля.
280: if ( $TableName == 'postcalc_light_cities' ) {
281: $KeyField = 'city';
282: $OrderField = 'city';
283: } elseif ( $TableName == 'postcalc_light_post_indexes' ) {
284: $KeyField = 'pindex';
285: $OrderField = 'pindex';
286: } elseif ( $TableName == 'postcalc_light_countries' ) {
287: $KeyField = 'iso2';
288: $OrderField = 'country';
289: }
290: $Limit = ( $limit ) ? "LIMIT $limit" : '';
291: $Where = ( $search ) ? "WHERE $KeyField LIKE '$search%'" : '' ;
292: $Order = "ORDER BY $OrderField";
293: echo "SELECT * FROM $TableName $Where $Limit\n";
294: // Устанавливаем правильный набор символов для запроса MySQL
295: if ( stripos($config_cs, 'utf') !== false ) {
296: $MySQL_Charset = 'UTF8';
297: } else if ( stripos($config_cs, '1251') !== false ) {
298: $MySQL_Charset = 'cp1251';
299: }
300: $stmt = $mysql->query("SET NAMES $MySQL_Charset");
301: $stmt = $mysql->query("SELECT * FROM $TableName $Where $Order $Limit");
302: while ( $row = $stmt->fetch_row() )
303: $arr[$row[0]] = $row[1];
304: $mysql->close();
305: return $arr;
306: }
307: // === Источник - текстовый файл.
308: $src_idx = basename($src_txt, 'txt'). 'idx';
309: $src_txt = $config_txt_dir. '/' .$src_txt;
310: $src_idx = $config_txt_dir. '/' .$src_idx;
311: $search = mb_convert_case($search, MB_CASE_LOWER, $config_cs);
312:
313: // == Если нет файла индекса или фильтр отсутствует, идем полным перебором
314: if ( !file_exists($src_idx) || $search == '' ) {
315: $fp = fopen($src_txt, 'r');
316: $counter = 0;
317: while ( ( $line = fgets($fp) ) !== false ) {
318: list($key, $value) = explode("\t", $line);
319: if ( $search == '' ||
320: ( $search != '' && mb_stripos($key, $search, 0, $config_cs) === 0 )
321: ) {
322: $arr[$key] = trim($value);
323: if ($limit > 0 && ++$counter >= $limit) break;
324: }
325: }
326: fclose($fp);
327: return $arr;
328: }
329: // == Индексный файл есть.
330: $string_idx = file_get_contents($src_idx);
331: // Начало совпадения
332: $pos = mb_strpos(
333: $string_idx,
334: // Берем два первых символа
335: "\n".mb_substr($search, 0, 2, $config_cs),
336: 0,
337: $config_cs
338: );
339: $idx_len = mb_strlen($string_idx, $config_cs);
340: // Конец строки с совпадением
341: $pos_line_end = mb_strpos($string_idx, "\n", $pos + 1, $config_cs);
342: $s = mb_substr($string_idx, $pos + 1, $pos_line_end - $pos - 1, $config_cs);
343: // Получили сдвиг в оригинальном файле.
344: list($tmp, $offset) = explode("\t", $s);
345: // Теперь длина.
346: if ( $idx_len == $pos_line_end + 1 ) {
347: // Если это последняя строка в файле индекса, то будем брать фрагмент до конца файла данных.
348: // Берем любое большое число.
349: $len = 1000000;
350: } else {
351: $pos = $pos_line_end + 1;
352: $pos_line_end = mb_strpos($string_idx, "\n", $pos + 1, $config_cs);
353: $s = mb_substr($string_idx, $pos + 1, $pos_line_end - $pos, $config_cs);
354: list($tmp, $offset2) = explode("\t", $s);
355: $len = $offset2 - $offset;
356: }
357: $fp = fopen($src_txt, 'r');
358: fseek($fp, $offset);
359: $chunk = fread($fp, $len);
360: fclose($fp);
361: // Теперь делаем массив.
362: $arr_tmp = explode("\n", trim($chunk));
363: $counter = 0;
364: foreach ($arr_tmp as $no => $line ) {
365: list($key, $value) = explode("\t", $line);
366: if ( $search == '' ||
367: ( $search != '' && mb_stripos($key, $search, 0, $config_cs) === 0 )
368: ) {
369: $arr[$key] = trim($value);
370: if ($limit > 0 && ++$counter >= $limit) break;
371: }
372:
373: }
374:
375: return $arr;
376: }
377:
378: /**
379: * Вспомогательная функция, генерирует из массива содержимое списка <select> для веб-страницы.
380: *
381: * Создает список стран, Россия в списке выделена:
382: * <code>
383: * postcalc_make_select(postcalc_arr_from_txt('postcalc_light_countries.txt'),'RU');
384: * </code>
385: *
386: * @ignore
387: * @param array $arrList Ключи массива становятся value в тэге <option>, значения массива становятся видимыми элементами списка.
388: * @param string $defaultValue Это значение будет выделено (атрибут selected).
389: * @return string Готовый список для вставки на веб-странице между тэгами <select> и </select>.
390: */
391: function postcalc_make_select($arrList,$defaultValue)
392: {
393: $Out='';
394: foreach ($arrList as $value=>$label) {
395: $Out.= "<option value='$value'";
396: $Out.= ($value == $defaultValue) ? ' selected' : '';
397: $Out.= ">$label</option>\n";
398: }
399: return $Out;
400: }
401: /**
402: * Автодополнение для полей "Откуда" и "Куда" на веб-странице. Работает с виджетом jQuery Autocomplete.
403: *
404: * Внимание! Входные данные ожидаются всегда в кодировке UTF-8.
405: * jQuery Autocomplete эту кодировку обеспечивает автоматически, в остальных случаях можно применять
406: * функцию javascript encodeURIComponent().
407: *
408: * Возвращает массив JSON для непосредственного использования в виджете jQuery Autocomplete в кодировке UTF-8.
409: *
410: *
411: * @param string $post_index Начало почтового индекса или населенного пункта.
412: * @param integer $limit Максимальное число элементов в списке.
413: * @return mixed Объект JSON для непосредственного использования в виджете jQuery Autocomplete.
414: *
415: * @uses postcalc_arr_from_txt() Запрашивает функцию postcalc_arr_from_txt() для получения массива, сгенерированного из текстового файла.
416: */
417: function postcalc_autocomplete($post_index, $limit = 10)
418: {
419: global $arrPostcalcConfig;
420: $Charset = $arrPostcalcConfig['cs'];
421: $arr = array();
422: // Не менее 3 начальных символов должны быть цифрами
423: if ( preg_match("/\d{3,}/", $post_index) ) {
424: $arr_indexes = postcalc_arr_from_txt('postcalc_light_post_indexes.txt', $post_index, $limit);
425: foreach ($arr_indexes as $pindex => $opsname)
426: $arr[] = array(
427: 'label' => $pindex.' '.mb_convert_encoding($opsname, 'UTF-8', $Charset),
428: 'value' => $pindex
429: );
430: } else {
431: // Все данные с веб-страницы поступают в UTF-8
432: $post_index = mb_convert_case($post_index, MB_CASE_TITLE, 'UTF-8');
433: // Преобразуем в текущую кодировку библиотеки
434: $post_index = mb_convert_encoding($post_index,$Charset, 'UTF-8' );
435: $arr_cities = postcalc_arr_from_txt('postcalc_light_cities.txt', $post_index, $limit);
436:
437:
438: foreach ($arr_cities as $city => $default_ops) {
439: $arr[]=array(
440: 'label' => mb_convert_encoding($city, 'UTF-8', $Charset),
441: 'value' => mb_convert_encoding($city, 'UTF-8', $Charset)
442: );
443: }
444:
445: }
446:
447: return json_encode($arr);
448: }
449:
450: /**
451: * Функция генерирует из журналов соединений массив PHP. Используется в postcalc_light_stat.php.
452: * Возвращаемый массив может быть использован и для самостоятельного анализа.
453: *
454: * Открывает в цикле все файлы, которые расположены в cache_dir и имеют название вида postcalc_light_YYYY-MM.log,
455: * возвращает массив, где данные сгруппированы по дням: ключ массива - дата в формате YYYY-MM-DD,
456: * значения: число обращений за сутки num_requests, среднее время запроса time_elapsed, средний размер ответа size.
457: *
458: * @global array $arrPostcalcConfig
459: * @return array
460: */
461: function postcalc_get_stat_arr() {
462: global $arrPostcalcConfig;
463: $postcalc_config_cache_dir = $arrPostcalcConfig['cache_dir'];
464: $arrStat=array();
465: foreach (glob("$postcalc_config_cache_dir/postcalc_light_*.log") as $logfile ) {
466: $fp_log=fopen($logfile,'r');
467: while ( $logline = fgets($fp_log)) {
468: list($date_time,$server,$time_elapsed,$size,$query_string)=explode("\t",$logline);
469: $date = substr($date_time, 0, 10);
470: if ( isset($arrStat[$date]) ) {
471: $arrStat[$date]['time_elapsed'] += $time_elapsed;
472: $arrStat[$date]['size'] += $size;
473: $arrStat[$date]['num_requests']++;
474: } else {
475: $arrStat[$date]['time_elapsed'] = $time_elapsed;
476: $arrStat[$date]['size'] = $size;
477: $arrStat[$date]['num_requests'] = 1;
478: $arrStat[$date]['errors'] = 0;
479: }
480: }
481: fclose($fp_log);
482: }
483: // Дополняем статистикой по ошибкам
484: foreach (glob("$postcalc_config_cache_dir/postcalc_error_*.log") as $logfile ) {
485: $fp_log=fopen($logfile,'r');
486: while ( $logline = fgets($fp_log)) {
487: list($date_time,$server,$time_elapsed,$error_short,$error_full)=explode("\t",$logline);
488: $date = substr($date_time, 0, 10);
489: if ( isset($arrStat[$date]['errors']) ) {
490: $arrStat[$date]['errors'] += 1;
491: } else {
492: $arrStat[$date]['errors'] = 1;
493: }
494: }
495: fclose($fp_log);
496: }
497:
498: // Теперь проходимся по всему массиву и вычисляем среднее арифметическое
499: // для size (округляем до целого) и time_elapsed (оставляем 3 знака после запятой).
500: foreach ( $arrStat as $date => $arr_values ) {
501: if ( isset($arrStat[$date]['time_elapsed']) )
502: $arrStat[$date]['time_elapsed'] = number_format(($arrStat[$date]['time_elapsed']/$arrStat[$date]['num_requests']),3,'.','');
503: if ( isset($arrStat[$date]['size']) )
504: $arrStat[$date]['size'] = round($arrStat[$date]['size']/$arrStat[$date]['num_requests'], 0);
505: }
506:
507: return $arrStat;
508: }
509: