2007-05-21 03:34:53 +04:00
|
|
|
|
#!/usr/bin/perl
|
2009-12-09 03:09:56 +03:00
|
|
|
|
# Всякая полезная фигня с минимумом жёстких зависимостей
|
2011-01-18 16:26:21 +03:00
|
|
|
|
# $Id$
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
|
|
|
|
package VMX::Common;
|
|
|
|
|
|
2009-03-13 02:25:03 +03:00
|
|
|
|
use utf8;
|
2008-09-23 20:14:01 +04:00
|
|
|
|
use strict;
|
2008-02-21 23:56:43 +03:00
|
|
|
|
use Encode;
|
2008-10-13 02:49:29 +04:00
|
|
|
|
|
2009-10-27 02:31:10 +03:00
|
|
|
|
use constant {
|
2009-11-02 17:31:15 +03:00
|
|
|
|
HASHARRAY => {Slice=>{}},
|
2009-10-27 02:31:10 +03:00
|
|
|
|
TS_UNIX => 0,
|
|
|
|
|
TS_DB => 1,
|
2009-12-02 21:21:37 +03:00
|
|
|
|
TS_DB_DATE => 2,
|
|
|
|
|
TS_MW => 3,
|
|
|
|
|
TS_EXIF => 4,
|
|
|
|
|
TS_ORACLE => 5,
|
|
|
|
|
TS_ISO_8601 => 6,
|
|
|
|
|
TS_RFC822 => 7,
|
2009-10-27 02:31:10 +03:00
|
|
|
|
};
|
2008-10-13 02:49:29 +04:00
|
|
|
|
|
2007-05-21 03:34:53 +04:00
|
|
|
|
require Exporter;
|
|
|
|
|
|
2009-10-27 02:31:10 +03:00
|
|
|
|
our @EXPORT = qw(
|
|
|
|
|
HASHARRAY
|
2009-12-02 21:21:37 +03:00
|
|
|
|
TS_UNIX TS_MW TS_DB TS_DB_DATE TS_EXIF TS_ORACLE TS_ISO_8601 TS_RFC822
|
2009-10-27 02:31:10 +03:00
|
|
|
|
);
|
2008-09-23 20:14:01 +04:00
|
|
|
|
our @EXPORT_OK = qw(
|
2009-03-13 02:25:03 +03:00
|
|
|
|
HASHARRAY quotequote min max trim htmlspecialchars strip_tags strip_unsafe_tags
|
2009-11-04 01:45:14 +03:00
|
|
|
|
file_get_contents dbi_hacks ar1el filemd5 mysql_quote updaterow_hashref updateall_hashref
|
2009-10-29 00:37:36 +03:00
|
|
|
|
insertall_arrayref insertall_hashref deleteall_hashref dumper_no_lf str2time callif urandom
|
2010-02-10 22:03:34 +03:00
|
|
|
|
normalize_url utf8on utf8off rfrom_to mysql2time mysqllocaltime resub requote
|
2010-09-29 17:41:38 +04:00
|
|
|
|
hashmrg litsplit strip_tagspace timestamp strlimit daemonize estrftime csv_read_record
|
2011-01-03 03:42:07 +03:00
|
|
|
|
sql_quote encode_json
|
2009-11-02 17:31:15 +03:00
|
|
|
|
), @EXPORT;
|
2008-09-23 20:14:01 +04:00
|
|
|
|
our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
|
2008-02-13 04:18:50 +03:00
|
|
|
|
|
2009-09-20 18:16:11 +04:00
|
|
|
|
# для strip_unsafe_tags()
|
2008-09-23 20:14:01 +04:00
|
|
|
|
our $allowed_html = [qw/
|
|
|
|
|
div span a b i u p h\d+ strike strong small big blink center ol pre sub
|
|
|
|
|
sup font br table tr td th tbody tfoot thead tt ul li em img marquee
|
|
|
|
|
/];
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-01-03 03:46:01 +03:00
|
|
|
|
our @DATE_INIT = ("Language=Russian", "DateFormat=non-US");
|
|
|
|
|
|
2010-07-04 15:56:11 +04:00
|
|
|
|
our $uri_escape_original;
|
2009-01-12 03:18:51 +03:00
|
|
|
|
|
2009-09-20 18:16:11 +04:00
|
|
|
|
# Exporter-ский импорт + подмена функций в DBI и URI::Escape
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub import
|
|
|
|
|
{
|
2009-01-02 17:18:57 +03:00
|
|
|
|
my @args = @_;
|
2009-01-12 03:18:51 +03:00
|
|
|
|
my $dbi_hacks = 0;
|
|
|
|
|
my $uri_escape_hacks = 0;
|
2009-11-02 17:31:15 +03:00
|
|
|
|
my $export = { map { $_ => 1 } @EXPORT };
|
2009-01-02 17:18:57 +03:00
|
|
|
|
foreach (@args)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2009-01-12 03:18:51 +03:00
|
|
|
|
if ($_ eq 'dbi_hacks')
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2009-01-12 03:18:51 +03:00
|
|
|
|
$_ = '!dbi_hacks';
|
|
|
|
|
$dbi_hacks = 1;
|
2008-09-06 02:38:55 +04:00
|
|
|
|
}
|
2009-01-12 03:18:51 +03:00
|
|
|
|
elsif ($_ eq 'uri_escape_hacks')
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2009-01-12 03:18:51 +03:00
|
|
|
|
$_ = '!uri_escape_hacks';
|
|
|
|
|
$uri_escape_hacks = 1;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
2009-11-02 17:31:15 +03:00
|
|
|
|
elsif (substr($_,0,1) eq '!' && $export->{substr($_,1)})
|
2009-03-13 02:25:03 +03:00
|
|
|
|
{
|
2009-11-02 17:31:15 +03:00
|
|
|
|
delete $export->{substr($_,1)};
|
2009-03-13 02:25:03 +03:00
|
|
|
|
}
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
2009-11-02 17:31:15 +03:00
|
|
|
|
push @args, keys %$export;
|
2009-01-12 03:18:51 +03:00
|
|
|
|
if ($dbi_hacks)
|
|
|
|
|
{
|
2009-09-20 18:16:11 +04:00
|
|
|
|
require DBI;
|
2009-01-12 03:18:51 +03:00
|
|
|
|
*DBI::_::st::fetchall_hashref = *VMX::Common::fetchall_hashref;
|
|
|
|
|
*DBI::st::fetchall_hashref = *VMX::Common::fetchall_hashref;
|
|
|
|
|
$DBI::DBI_methods{st}{fetchall_hashref} = { U =>[1,2,'[ $key_field ]'] };
|
|
|
|
|
$DBI::DBI_methods{db}{selectall_hashref} = { U =>[2,0,'$statement [, $keyfield [, \%attr [, @bind_params ] ] ]'], O=>0x2000 };
|
|
|
|
|
}
|
|
|
|
|
if ($uri_escape_hacks)
|
|
|
|
|
{
|
2009-09-20 18:16:11 +04:00
|
|
|
|
require URI::Escape;
|
2010-07-04 15:56:11 +04:00
|
|
|
|
$VMX::Common::uri_escape_original = \&URI::Escape::uri_escape;
|
2009-01-12 03:18:51 +03:00
|
|
|
|
*URI::Escape::uri_escape = *VMX::Common::uri_escape;
|
|
|
|
|
}
|
|
|
|
|
$Exporter::ExportLevel = 1;
|
2009-01-02 17:18:57 +03:00
|
|
|
|
my $r = Exporter::import(@args);
|
2009-01-12 03:18:51 +03:00
|
|
|
|
$Exporter::ExportLevel = 0;
|
|
|
|
|
return $r;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# Функция возвращает минимальное из значений
|
|
|
|
|
# $r = min (@list)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub min
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
return undef if (@_ < 1);
|
|
|
|
|
my $r = shift;
|
|
|
|
|
foreach (@_) { $r = $_ if $r > $_; }
|
|
|
|
|
return $r;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# Функция возвращает максимальное из значений
|
|
|
|
|
# $r = max (@list)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub max
|
|
|
|
|
{
|
2007-05-30 19:40:07 +04:00
|
|
|
|
return undef if (@_ < 1);
|
|
|
|
|
my $r = shift;
|
|
|
|
|
foreach (@_) { $r = $_ if $r < $_; }
|
|
|
|
|
return $r;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# ar1el($a) - аналог ($a || [])->[0], только ещё проверяет, что $a есть arrayref
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub ar1el
|
|
|
|
|
{
|
2009-01-12 03:18:51 +03:00
|
|
|
|
return undef unless 'ARRAY' eq ref $_[0];
|
|
|
|
|
return shift @{$_[0]};
|
2007-05-25 03:13:23 +04:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# Функция обрезает пробельные символы в начале и конце строки
|
2009-01-31 17:55:00 +03:00
|
|
|
|
# trim ($r)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub trim
|
|
|
|
|
{
|
2008-10-19 23:55:28 +04:00
|
|
|
|
local $_ = $_[0];
|
2009-01-31 17:55:00 +03:00
|
|
|
|
if ($_[1])
|
|
|
|
|
{
|
|
|
|
|
s/^$_[1]//s;
|
|
|
|
|
s/$_[1]$//s;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s/^\s+//so;
|
|
|
|
|
s/\s+$//so;
|
|
|
|
|
}
|
2008-09-07 01:18:12 +04:00
|
|
|
|
$_;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# аналог HTML::Entities::encode_entities
|
|
|
|
|
# $str = htmlspecialchars ($str)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub htmlspecialchars
|
|
|
|
|
{
|
2008-10-19 23:55:28 +04:00
|
|
|
|
local $_ = $_[0];
|
2009-02-08 04:51:39 +03:00
|
|
|
|
s/&/&/gso;
|
2008-09-06 02:38:55 +04:00
|
|
|
|
s/</</gso;
|
|
|
|
|
s/>/>/gso;
|
|
|
|
|
s/\"/"/gso;
|
|
|
|
|
s/\'/'/gso;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
return $_;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# удаление тегов из строки, кроме заданных
|
|
|
|
|
# $str = strip_tags ($str)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub strip_tags
|
|
|
|
|
{
|
2008-10-19 23:55:28 +04:00
|
|
|
|
local $_ = shift;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
my $ex = join '|', @{(shift || [])};
|
|
|
|
|
$ex = "(?!/?($ex))" if $ex;
|
2009-01-29 18:50:48 +03:00
|
|
|
|
s/<\/?$ex(!?[a-z0-9_\-]+)[^<>]*>//gis;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
return $_;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-19 03:37:05 +04:00
|
|
|
|
# преобразование \s+ и тегов в 1 пробел
|
|
|
|
|
sub strip_tagspace
|
|
|
|
|
{
|
|
|
|
|
local $_ = shift;
|
|
|
|
|
my $ex = join '|', @{(shift || [])};
|
|
|
|
|
$ex = "(?!/?($ex))" if $ex;
|
|
|
|
|
s/\s*(<\/?$ex(!?[a-z0-9_\-]+)[^<>]*>\s*)+/ /gis;
|
|
|
|
|
s/\s+/ /gis;
|
|
|
|
|
return $_;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# удаление небезопасных HTML тегов (всех кроме our $allowed_html)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub strip_unsafe_tags
|
|
|
|
|
{
|
2008-09-23 20:14:01 +04:00
|
|
|
|
strip_tags($_[0], $allowed_html);
|
2008-09-06 02:38:55 +04:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# аналог File::Slurp
|
|
|
|
|
# $contents = file_get_contents ($filename)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub file_get_contents
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my ($tmp, $res);
|
|
|
|
|
open ($tmp, '<'.$_[0]);
|
2008-09-06 02:38:55 +04:00
|
|
|
|
if ($tmp)
|
|
|
|
|
{
|
2009-01-12 03:18:51 +03:00
|
|
|
|
local $/ = undef;
|
2007-05-25 22:14:24 +04:00
|
|
|
|
$res = <$tmp>;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
close ($tmp);
|
|
|
|
|
}
|
|
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# изменённый вариант функции DBI::_::st::fetchall_hashref
|
2009-03-13 02:25:03 +03:00
|
|
|
|
# первая вещь - аналог fetchall_arrayref(HASHARRAY), т.е. просто возвращает
|
2008-10-19 00:56:22 +04:00
|
|
|
|
# массив хешей при передаче в качестве $key_field ссылки на пустой массив или undef.
|
|
|
|
|
# вторая вещь - о которой все мы, пользователи MySQL, давно мечтали - возможность
|
|
|
|
|
# сделать SELECT t1.*, t2.*, t3.* и при этом успешно разделить поля таблиц,
|
|
|
|
|
# распределив их по отдельным хешам.
|
|
|
|
|
# весь смысл в том, что при передаче в качестве $key_field хеша делает из каждой
|
2009-01-02 17:18:57 +03:00
|
|
|
|
# строчки вложенный hashref или arrayref, а колонки из результата запроса разделяет
|
|
|
|
|
# по $key_field->{Separator} или '_' по умолчанию.
|
2008-10-19 00:56:22 +04:00
|
|
|
|
# то есть например $dbh->selectall_hashref(
|
|
|
|
|
# "SELECT t1.*, 0 AS `_`, t2.* FROM t1 JOIN t2 USING (join_field)",
|
2009-01-02 17:18:57 +03:00
|
|
|
|
# { Separator => '_', Multi => [ 't1', 't2' ] }, {}
|
2008-10-19 00:56:22 +04:00
|
|
|
|
# ) вернёт ссылку на массив хешрефов вида { t1 => { ... }, t2 => { ... } },
|
2009-01-02 17:18:57 +03:00
|
|
|
|
# а если в качестве Multi передать просто скаляр, являющийся истиной (напр. 1),
|
|
|
|
|
# то вернёт ссылку на массив массивов вида [ { ... }, { ... } ].
|
2008-10-19 00:56:22 +04:00
|
|
|
|
# т.е. поля t1 и t2 будут разделены по подхешам даже в случае, если в t1 и t2
|
|
|
|
|
# существуют поля с одинаковыми именами
|
|
|
|
|
# кроме того, кэширует все свои вспомогательные массивы в объекте запроса
|
|
|
|
|
# для дополнительной оптимальности
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub fetchall_hashref
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my ($sth, $key_field) = @_;
|
2008-10-19 00:56:22 +04:00
|
|
|
|
return multifetchall_hashref($sth, $key_field) if ref($key_field) eq 'HASH';
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $hash_key_name = $sth->{FetchHashKeyName} || 'NAME';
|
|
|
|
|
my $names_hash = $sth->FETCH("${hash_key_name}_hash");
|
|
|
|
|
my @key_fields = (ref $key_field) ? @$key_field : $key_field ? ($key_field) : ();
|
2008-10-19 00:56:22 +04:00
|
|
|
|
my $cachename = "__cache_key_fields_".join "_", @key_fields;
|
|
|
|
|
my $key_indexes = $sth->{$cachename};
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $num_of_fields = $sth->FETCH('NUM_OF_FIELDS');
|
2008-10-19 00:56:22 +04:00
|
|
|
|
unless ($key_indexes)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2008-10-19 00:56:22 +04:00
|
|
|
|
$key_indexes = [];
|
|
|
|
|
foreach (@key_fields)
|
|
|
|
|
{
|
|
|
|
|
my $index = $names_hash->{$_}; # perl index not column
|
|
|
|
|
$index = $_ - 1 if !defined $index && DBI::looks_like_number($_) && $_ >= 1 && $_ <= $num_of_fields;
|
|
|
|
|
return $sth->set_err(1, "Field '$_' does not exist (not one of @{[keys %$names_hash]})")
|
|
|
|
|
unless defined $index;
|
|
|
|
|
push @$key_indexes, $index;
|
|
|
|
|
}
|
|
|
|
|
$sth->{$cachename} = $key_indexes;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
my $rows = {};
|
2008-10-19 00:56:22 +04:00
|
|
|
|
$rows = [] unless scalar @key_fields;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $NAME = $sth->FETCH($hash_key_name);
|
|
|
|
|
my @row = (undef) x $num_of_fields;
|
|
|
|
|
$sth->bind_columns(\(@row)) if @row;
|
2008-10-19 00:56:22 +04:00
|
|
|
|
my $ref;
|
|
|
|
|
if (scalar @key_fields)
|
|
|
|
|
{
|
|
|
|
|
while ($sth->fetch)
|
|
|
|
|
{
|
|
|
|
|
$ref = $rows;
|
|
|
|
|
$ref = $ref->{$row[$_]} ||= {} for @$key_indexes;
|
|
|
|
|
@$ref{@$NAME} = @row;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
while ($sth->fetch)
|
|
|
|
|
{
|
|
|
|
|
push @$rows, $ref = {};
|
|
|
|
|
@$ref{@$NAME} = @row;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $rows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# вот здесь-то и реализовано вертикальное разбиение результата
|
|
|
|
|
sub multifetchall_hashref
|
|
|
|
|
{
|
|
|
|
|
my ($sth, $key_field) = @_;
|
2009-01-02 17:18:57 +03:00
|
|
|
|
$key_field = [] unless
|
|
|
|
|
ref($key_field->{Multi}) eq 'ARRAY' ||
|
|
|
|
|
$key_field->{Multi} && !ref $key_field->{Multi};
|
2008-10-19 00:56:22 +04:00
|
|
|
|
return fetchall_hashref($sth, $key_field) if ref($key_field) ne 'HASH';
|
|
|
|
|
my $NAME = $sth->FETCH($sth->{FetchHashKeyName} || 'NAME');
|
|
|
|
|
my $num_of_fields = $sth->FETCH('NUM_OF_FIELDS');
|
|
|
|
|
my $cachename = "__cache_multi_key_fields";
|
|
|
|
|
my ($nh, $ni, $i, $hs);
|
|
|
|
|
unless ($sth->{$cachename})
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2008-10-19 00:56:22 +04:00
|
|
|
|
# массивы индексов и имён ещё не построены, построим
|
|
|
|
|
my $split = $key_field->{Separator} || '_';
|
|
|
|
|
$nh = [[]];
|
|
|
|
|
$ni = [[]];
|
|
|
|
|
$i = 0;
|
|
|
|
|
for my $k (0..$#$NAME)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2008-10-19 00:56:22 +04:00
|
|
|
|
if ($NAME->[$k] eq $split)
|
|
|
|
|
{
|
|
|
|
|
$i++;
|
|
|
|
|
$nh->[$i] = [];
|
|
|
|
|
$ni->[$i] = [];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
push @{$nh->[$i]}, $NAME->[$k];
|
|
|
|
|
push @{$ni->[$i]}, $k;
|
|
|
|
|
}
|
2008-09-06 02:38:55 +04:00
|
|
|
|
}
|
2008-10-19 00:56:22 +04:00
|
|
|
|
$sth->{$cachename} = [ $nh, $ni ];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
($nh, $ni) = @{$sth->{$cachename}};
|
|
|
|
|
}
|
|
|
|
|
my $rows = [];
|
|
|
|
|
my @row = (undef) x $num_of_fields;
|
|
|
|
|
$sth->bind_columns(\(@row)) if @row;
|
|
|
|
|
$hs = $key_field->{Multi};
|
|
|
|
|
my $ref;
|
2009-01-02 17:18:57 +03:00
|
|
|
|
if (ref $hs) # если передана ссылка на массив - это имена в хеше
|
2008-10-19 00:56:22 +04:00
|
|
|
|
{
|
2009-01-02 17:18:57 +03:00
|
|
|
|
while ($sth->fetch)
|
2008-09-06 02:38:55 +04:00
|
|
|
|
{
|
2009-01-02 17:18:57 +03:00
|
|
|
|
push @$rows, $ref = {};
|
|
|
|
|
for $i (0..$#$hs)
|
|
|
|
|
{
|
|
|
|
|
$ref->{$hs->[$i]} = {};
|
|
|
|
|
@{$ref->{$hs->[$i]}}{@{$nh->[$i]}} = @row[@{$ni->[$i]}];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else # иначе это будут вложенные массивы
|
|
|
|
|
{
|
|
|
|
|
while ($sth->fetch)
|
|
|
|
|
{
|
|
|
|
|
push @$rows, $ref = [];
|
|
|
|
|
for $i (0..$#$ni)
|
|
|
|
|
{
|
|
|
|
|
$ref->[$i] = {};
|
|
|
|
|
@{$ref->[$i]}{@{$nh->[$i]}} = @row[@{$ni->[$i]}];
|
|
|
|
|
}
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $rows;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# Обновить все строки, у которых значения полей с названиями ключей %$key
|
|
|
|
|
# равны значениям %$key, установив в них поля с названиями ключей %$row
|
|
|
|
|
# значениям %$row
|
2008-09-06 02:38:55 +04:00
|
|
|
|
sub updaterow_hashref
|
|
|
|
|
{
|
2008-02-13 04:18:50 +03:00
|
|
|
|
my ($dbh, $table, $row, $key) = @_;
|
|
|
|
|
return 0 unless
|
2008-09-23 20:14:01 +04:00
|
|
|
|
$dbh && $table &&
|
2008-02-13 04:18:50 +03:00
|
|
|
|
$row && ref($row) eq 'HASH' && %$row &&
|
2009-10-29 00:37:36 +03:00
|
|
|
|
$key && (ref($key) eq 'HASH' && %$key || $key eq '1');
|
2008-02-13 04:18:50 +03:00
|
|
|
|
my @f = keys %$row;
|
2009-10-29 00:37:36 +03:00
|
|
|
|
my @bind = @$row{@f};
|
|
|
|
|
my $sql = 'UPDATE `'.$table.'` SET '.join(', ', map { "`$_`=?" } @f);
|
|
|
|
|
if ($key ne 1)
|
|
|
|
|
{
|
|
|
|
|
my @k = keys %$key;
|
|
|
|
|
$sql .= ' WHERE '.join(' AND ', map { "`$_`=?" } @k);
|
|
|
|
|
push @bind, @$key{@k};
|
|
|
|
|
}
|
|
|
|
|
return $dbh->do($sql, undef, @bind);
|
2008-02-13 04:18:50 +03:00
|
|
|
|
}
|
|
|
|
|
|
2009-06-27 17:45:22 +04:00
|
|
|
|
# Множественный UPDATE - обновить много строк @%$rows,
|
|
|
|
|
# но только по первичному ключу (каждая строка должна содержать его значение!)
|
|
|
|
|
sub updateall_hashref
|
|
|
|
|
{
|
|
|
|
|
my ($dbh, $table, $rows) = @_;
|
|
|
|
|
my @f = keys %{$rows->[0]};
|
|
|
|
|
my $sql = "INSERT INTO `$table` (`".join("`,`",@f)."`) VALUES ".
|
|
|
|
|
join(",",("(".(join(",", ("?") x scalar(@f))).")") x scalar(@$rows)).
|
|
|
|
|
" ON DUPLICATE KEY UPDATE ".join(',', map { "`$_`=VALUES(`$_`)" } @f);
|
|
|
|
|
my @bind = map { @$_{@f} } @$rows;
|
2009-10-29 00:37:36 +03:00
|
|
|
|
return $dbh->do($sql, undef, @bind);
|
2009-06-27 17:45:22 +04:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-29 23:45:54 +03:00
|
|
|
|
# Удалить все строки, у которых значения полей с названиями ключей %$key
|
|
|
|
|
# равны значениям %$key
|
|
|
|
|
sub deleteall_hashref
|
|
|
|
|
{
|
|
|
|
|
my ($dbh, $table, $key) = @_;
|
|
|
|
|
return 0 unless $dbh && $table &&
|
|
|
|
|
$key && ref($key) eq 'HASH' && %$key;
|
2009-01-31 17:55:00 +03:00
|
|
|
|
my $sql = [];
|
|
|
|
|
my @bind;
|
|
|
|
|
foreach (keys %$key)
|
|
|
|
|
{
|
|
|
|
|
if (!defined $key->{$_})
|
|
|
|
|
{
|
|
|
|
|
push @$sql, "`$_` IS NULL";
|
|
|
|
|
}
|
|
|
|
|
elsif (!ref $key->{$_})
|
|
|
|
|
{
|
|
|
|
|
push @$sql, "`$_`=?";
|
|
|
|
|
push @bind, $key->{$_};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return unless @{$key->{$_}};
|
|
|
|
|
# IN (?, ?, ?, ..., ?)
|
|
|
|
|
push @$sql, "`$_` IN (" . join(",", ("?") x @{$key->{$_}}) . ")";
|
|
|
|
|
push @bind, @{$key->{$_}};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$sql = "DELETE FROM `$table` WHERE " . join " AND ", @$sql;
|
2009-10-29 00:37:36 +03:00
|
|
|
|
return $dbh->do($sql, undef, @bind);
|
2008-10-29 23:45:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# Вставить набор записей $rows = [{},{},{},...] в таблицу $table
|
|
|
|
|
# Возможно после этого дополнить каждую запись $reselect полями (напр. '*'),
|
|
|
|
|
# сделав дополнительный запрос выборки. Для этого требуются ещё поля
|
|
|
|
|
# `ji` INT DEFAULT NULL и `jin` INT DEFAULT NULL, и индекс по ним.
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub insertall_hashref
|
|
|
|
|
{
|
2010-07-04 15:56:11 +04:00
|
|
|
|
my ($dbh, $table, $rows, $reselect, $replace, $update) = @_;
|
2008-02-13 04:18:50 +03:00
|
|
|
|
return 0 unless
|
2009-10-29 00:37:36 +03:00
|
|
|
|
$dbh && $table &&
|
2008-02-13 04:18:50 +03:00
|
|
|
|
$rows && ref($rows) eq 'ARRAY' && @$rows;
|
2008-09-02 17:30:08 +04:00
|
|
|
|
my $conn_id = undef;
|
2010-07-04 15:56:11 +04:00
|
|
|
|
$reselect = undef if $update;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
if ($reselect)
|
|
|
|
|
{
|
2008-02-13 04:18:50 +03:00
|
|
|
|
my $i = 0;
|
2009-08-13 03:00:28 +04:00
|
|
|
|
$conn_id = $dbh->{mysql_thread_id};
|
2008-09-02 17:30:08 +04:00
|
|
|
|
@$_{'ji','jin'} = ($conn_id, ++$i) foreach @$rows;
|
2008-02-13 04:18:50 +03:00
|
|
|
|
}
|
|
|
|
|
my @f = keys %{$rows->[0]};
|
2008-10-19 13:59:15 +04:00
|
|
|
|
my $sql = ($replace ? 'REPLACE' : 'INSERT').
|
2008-09-23 20:14:01 +04:00
|
|
|
|
' INTO `'.$table.'` (`'.join('`,`',@f).'`) VALUES '.
|
2008-02-13 04:18:50 +03:00
|
|
|
|
join(',',('('.(join(',', ('?') x scalar(@f))).')') x scalar(@$rows));
|
2010-07-04 15:56:11 +04:00
|
|
|
|
$sql .= ' ON DUPLICATE KEY UPDATE '.join(', ', map { "`$_`=VALUES(`$_`)" } @f) if $update;
|
2008-02-13 04:18:50 +03:00
|
|
|
|
my @bind = map { @$_{@f} } @$rows;
|
2009-10-29 00:37:36 +03:00
|
|
|
|
my $st = $dbh->do($sql, undef, @bind);
|
2008-02-13 04:18:50 +03:00
|
|
|
|
return $st if !$st || !$reselect;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
if (ref($reselect) eq 'ARRAY')
|
|
|
|
|
{
|
2008-02-13 04:18:50 +03:00
|
|
|
|
$reselect = '`'.join('`,`',@$reselect).'`';
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
|
|
|
|
elsif ($reselect ne '*')
|
|
|
|
|
{
|
2008-02-13 04:18:50 +03:00
|
|
|
|
$reselect = "`$reselect`";
|
|
|
|
|
}
|
2008-02-14 03:39:17 +03:00
|
|
|
|
# осуществляем reselect данных
|
2008-09-23 20:14:01 +04:00
|
|
|
|
$sql = "SELECT $reselect FROM `$table` WHERE `ji`=? ORDER BY `jin` ASC";
|
2008-09-02 17:30:08 +04:00
|
|
|
|
@bind = ($conn_id);
|
2009-03-13 02:25:03 +03:00
|
|
|
|
my $resel = $dbh->selectall_arrayref($sql, HASHARRAY, @bind) || [];
|
2008-08-15 21:31:24 +04:00
|
|
|
|
for (my $i = 0; $i < @$resel; $i++)
|
|
|
|
|
{
|
2008-02-14 03:39:17 +03:00
|
|
|
|
$rows->[$i]->{$_} = $resel->[$i]->{$_} for keys %{$resel->[$i]};
|
|
|
|
|
}
|
2008-09-23 20:14:01 +04:00
|
|
|
|
$sql = "UPDATE `$table` SET `ji`=NULL, `jin`=NULL WHERE `ji`=?";
|
2009-10-29 00:37:36 +03:00
|
|
|
|
$dbh->do($sql, undef, @bind);
|
2008-02-14 03:39:17 +03:00
|
|
|
|
return $st;
|
2008-02-13 04:18:50 +03:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-29 00:37:36 +03:00
|
|
|
|
# то же, но массив и без reselectов
|
|
|
|
|
sub insertall_arrayref
|
|
|
|
|
{
|
|
|
|
|
my ($dbh, $table, $key, $rows, $replace) = @_;
|
|
|
|
|
return 0 unless
|
|
|
|
|
$dbh && $table &&
|
|
|
|
|
$rows && ref($rows) eq 'ARRAY' && @$rows &&
|
|
|
|
|
$key && ref($key) eq 'ARRAY' && @$key;
|
|
|
|
|
my $sql = ($replace ? 'REPLACE' : 'INSERT').
|
|
|
|
|
' INTO `'.$table.'` (`'.join('`,`', @$key).'`) VALUES ';
|
|
|
|
|
my $bind;
|
|
|
|
|
if (ref $rows->[0])
|
|
|
|
|
{
|
|
|
|
|
$bind = [ map { @$_ } @$rows ];
|
|
|
|
|
$sql .= join(',', ('('.(join(',', ('?') x scalar(@$key))).')') x scalar(@$rows));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$bind = $rows;
|
|
|
|
|
$sql .= join(',', ('('.(join(',', ('?') x scalar(@$key))).')') x int(@$rows/@$key));
|
|
|
|
|
}
|
|
|
|
|
return $dbh->do($sql, undef, @$bind);
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# вычисление MD5 хеша от файла
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub filemd5
|
|
|
|
|
{
|
2007-05-29 19:40:59 +04:00
|
|
|
|
my ($file) = @_;
|
|
|
|
|
my $f;
|
|
|
|
|
my $r;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
if (open $f, "<$file")
|
|
|
|
|
{
|
2009-09-20 18:16:11 +04:00
|
|
|
|
require Digest::MD5;
|
2007-05-29 19:40:59 +04:00
|
|
|
|
my $ctx = Digest::MD5->new;
|
|
|
|
|
$ctx->addfile($f);
|
|
|
|
|
$r = $ctx->hexdigest;
|
|
|
|
|
close $f;
|
|
|
|
|
}
|
|
|
|
|
return $r;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# тоже <ни фига не нужный велосипед>, экранирование символов для MySQL,
|
|
|
|
|
# да ещё и несколько кривое
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub mysql_quote
|
|
|
|
|
{
|
2009-01-12 03:18:51 +03:00
|
|
|
|
my ($a) = @_;
|
|
|
|
|
$a =~ s/\'/\'\'/gso;
|
2008-02-13 04:18:50 +03:00
|
|
|
|
$a =~ s/\\/\\\\/gso;
|
2009-01-12 03:18:51 +03:00
|
|
|
|
return "'$a'";
|
2007-06-07 03:09:26 +04:00
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# экранирование кавычек
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub quotequote
|
|
|
|
|
{
|
|
|
|
|
my ($a) = @_;
|
|
|
|
|
$a =~ s/\'|\"/\\$&/gso;
|
|
|
|
|
return $a;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-13 02:49:29 +04:00
|
|
|
|
# Dumper без переводов строки
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub dumper_no_lf
|
|
|
|
|
{
|
2008-03-13 17:04:57 +03:00
|
|
|
|
my $r = Data::Dumper::Dumper (@_);
|
|
|
|
|
$r =~ s/\s+/ /giso;
|
|
|
|
|
return $r;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-17 02:44:03 +04:00
|
|
|
|
# str2time, принимающий формат даты вида DD.MM.YYYY
|
2009-01-03 03:46:01 +03:00
|
|
|
|
my $init;
|
2009-03-25 20:20:51 +03:00
|
|
|
|
my $orig_DIRussian;
|
2008-10-17 02:44:03 +04:00
|
|
|
|
sub str2time
|
|
|
|
|
{
|
|
|
|
|
my ($str) = @_;
|
2009-01-03 03:46:01 +03:00
|
|
|
|
my $time;
|
2009-03-25 20:20:51 +03:00
|
|
|
|
unless ($init)
|
|
|
|
|
{
|
2009-09-20 18:16:11 +04:00
|
|
|
|
require Date::Manip;
|
2009-03-25 20:20:51 +03:00
|
|
|
|
$orig_DIRussian = \&Date::Manip::_Date_Init_Russian;
|
|
|
|
|
*Date::Manip::_Date_Init_Russian = \&date_init_russian;
|
2009-10-23 17:39:24 +04:00
|
|
|
|
Date::Manip::Date_Init(@DATE_INIT);
|
2009-03-25 20:20:51 +03:00
|
|
|
|
$init = 1;
|
|
|
|
|
}
|
2009-01-03 14:56:08 +03:00
|
|
|
|
$str = lc $str;
|
2009-10-23 17:39:24 +04:00
|
|
|
|
$time = Date::Manip::UnixDate(Date::Manip::ParseDate($str),"%s");
|
2009-01-03 03:46:01 +03:00
|
|
|
|
return $time if defined $time;
|
|
|
|
|
$time = $str;
|
|
|
|
|
$time =~ s/(\d{2})\.(\d{2})\.(\d{4})/$2\/$1\/$3/gso;
|
2009-09-20 18:16:11 +04:00
|
|
|
|
require Date::Parse;
|
2009-01-03 03:46:01 +03:00
|
|
|
|
$time = Date::Parse::str2time($time);
|
|
|
|
|
return $time;
|
2008-10-17 02:44:03 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-27 02:31:10 +03:00
|
|
|
|
my @Mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
|
|
|
|
|
my %mon = qw(jan 0 feb 1 mar 2 apr 3 may 4 jun 5 jul 6 aug 7 sep 8 oct 9 nov 10 dec 11);
|
|
|
|
|
my @Wday = qw(Sun Mon Tue Wed Thu Fri Sat);
|
|
|
|
|
|
|
|
|
|
# ограниченная распознавалка дат
|
|
|
|
|
sub timestamp
|
|
|
|
|
{
|
|
|
|
|
my ($ts, $format) = @_;
|
|
|
|
|
|
|
|
|
|
require POSIX;
|
2009-12-02 21:21:37 +03:00
|
|
|
|
if (int($ts) eq $ts)
|
2009-10-27 02:31:10 +03:00
|
|
|
|
{
|
2009-12-02 21:21:37 +03:00
|
|
|
|
# TS_UNIX or Epoch
|
|
|
|
|
$ts = time if !$ts;
|
2009-10-27 02:31:10 +03:00
|
|
|
|
}
|
2010-07-04 15:56:11 +04:00
|
|
|
|
|
|
|
|
|
elsif ($ts =~ /^\D*(\d{4,}?)\D*(\d{2})\D*(\d{2})\D*(?:(\d{2})\D*(\d{2})\D*(\d{2})\D*([\+\- ]\d{2}\D*)?)?$/so)
|
2009-10-27 02:31:10 +03:00
|
|
|
|
{
|
2009-12-02 21:21:37 +03:00
|
|
|
|
# TS_DB, TS_DB_DATE, TS_MW, TS_EXIF, TS_ISO_8601
|
|
|
|
|
$ts = POSIX::mktime($6||0, $5||0, $4||0, $3, $2-1, $1-1900);
|
2009-10-27 02:31:10 +03:00
|
|
|
|
}
|
|
|
|
|
elsif ($ts =~ /^\s*(\d\d?)-(...)-(\d\d(?:\d\d)?)\s*(\d\d)\.(\d\d)\.(\d\d)/so)
|
|
|
|
|
{
|
|
|
|
|
# TS_ORACLE
|
|
|
|
|
$ts = POSIX::mktime($6, $5, $4, int($1), $mon{lc $2}, $3 < 100 ? $3 : $3-1900);
|
|
|
|
|
}
|
|
|
|
|
elsif ($ts =~ /^\s*..., (\d\d?) (...) (\d{4,}) (\d\d):(\d\d):(\d\d)\s*([\+\- ]\d\d)\s*$/so)
|
|
|
|
|
{
|
|
|
|
|
# TS_RFC822
|
|
|
|
|
$ts = POSIX::mktime($6, $5, $4, int($1), $mon{lc $2}, $3-1900);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-12-02 21:21:37 +03:00
|
|
|
|
# Bogus value, return undef
|
|
|
|
|
return undef;
|
2009-10-27 02:31:10 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$format)
|
|
|
|
|
{
|
|
|
|
|
# TS_UNIX
|
|
|
|
|
return $ts;
|
|
|
|
|
}
|
|
|
|
|
elsif ($format == TS_MW)
|
|
|
|
|
{
|
|
|
|
|
return POSIX::strftime("%Y%m%d%H%M%S", localtime($ts));
|
|
|
|
|
}
|
|
|
|
|
elsif ($format == TS_DB)
|
|
|
|
|
{
|
|
|
|
|
return POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
|
|
|
|
|
}
|
2009-12-02 21:21:37 +03:00
|
|
|
|
elsif ($format == TS_DB_DATE)
|
|
|
|
|
{
|
|
|
|
|
return POSIX::strftime("%Y-%m-%d", localtime($ts));
|
|
|
|
|
}
|
2009-10-27 02:31:10 +03:00
|
|
|
|
elsif ($format == TS_ISO_8601)
|
|
|
|
|
{
|
|
|
|
|
return POSIX::strftime("%Y-%m-%dT%H:%M:%SZ", localtime($ts));
|
|
|
|
|
}
|
|
|
|
|
elsif ($format == TS_EXIF)
|
|
|
|
|
{
|
|
|
|
|
return POSIX::strftime("%Y:%m:%d %H:%M:%S", localtime($ts));
|
|
|
|
|
}
|
|
|
|
|
elsif ($format == TS_RFC822)
|
|
|
|
|
{
|
|
|
|
|
my @l = localtime($ts);
|
|
|
|
|
return POSIX::strftime($Wday[$l[6]].", %d ".$Mon[$l[4]]." %Y %H:%M:%S %z", @l);
|
|
|
|
|
}
|
|
|
|
|
elsif ($format == TS_ORACLE)
|
|
|
|
|
{
|
|
|
|
|
my @l = localtime($ts);
|
|
|
|
|
return POSIX::strftime("%d-".$Mon[$l[4]]."-%Y %H.%M.%S %p", @l);
|
|
|
|
|
}
|
|
|
|
|
return $ts;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-04 21:34:36 +04:00
|
|
|
|
# strftime с поддержкой %EB (месяц в родительном падеже)
|
|
|
|
|
my @month_gen = qw/Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря/;
|
|
|
|
|
sub estrftime
|
|
|
|
|
{
|
|
|
|
|
my $format = shift;
|
|
|
|
|
my $u;
|
|
|
|
|
my $m = $month_gen[$_[4]];
|
|
|
|
|
Encode::_utf8_on($m) if $u = Encode::is_utf8($format);
|
|
|
|
|
$format =~ s/\%EB/$m/gse;
|
|
|
|
|
my $r = POSIX::strftime($format, @_);
|
|
|
|
|
Encode::_utf8_on($r) if $u;
|
|
|
|
|
return $r;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-25 20:20:51 +03:00
|
|
|
|
sub date_init_russian
|
|
|
|
|
{
|
|
|
|
|
my $r = &$orig_DIRussian(@_);
|
|
|
|
|
rfrom_to($_[0], 'koi8-r', 'utf-8');
|
|
|
|
|
utf8on($_[0]);
|
|
|
|
|
$_[0]->{month_abb}->[1]->[2] = 'мар';
|
|
|
|
|
return $r;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-01 03:07:44 +03:00
|
|
|
|
# если значение - вернуть значение, если coderef - вызвать и вернуть значение
|
|
|
|
|
sub callif
|
|
|
|
|
{
|
|
|
|
|
my $sub = shift;
|
|
|
|
|
if (ref($sub) eq 'CODE')
|
|
|
|
|
{
|
|
|
|
|
return &$sub(@_);
|
|
|
|
|
}
|
|
|
|
|
elsif ($sub)
|
|
|
|
|
{
|
|
|
|
|
return $sub;
|
|
|
|
|
}
|
|
|
|
|
return wantarray ? () : undef;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-06 02:55:26 +04:00
|
|
|
|
# чтение N байт из Crypt::Random, urandom или rand() в случае его отсутствия
|
|
|
|
|
my $no_crypt_random;
|
2008-12-01 02:31:02 +03:00
|
|
|
|
sub urandom
|
|
|
|
|
{
|
|
|
|
|
my ($bs) = @_;
|
|
|
|
|
return undef unless $bs && $bs > 0;
|
2010-06-06 02:55:26 +04:00
|
|
|
|
if (!$no_crypt_random && !$INC{'Crypt/Random.pm'})
|
|
|
|
|
{
|
|
|
|
|
eval { require Crypt::Random; };
|
|
|
|
|
$no_crypt_random = 1 if $@;
|
|
|
|
|
}
|
|
|
|
|
if (!$no_crypt_random)
|
|
|
|
|
{
|
|
|
|
|
return Crypt::Random::makerandom_octet(Length => $bs, Strength => 1);
|
|
|
|
|
}
|
2008-12-01 02:31:02 +03:00
|
|
|
|
my ($fd, $data);
|
|
|
|
|
if (open $fd, "</dev/urandom")
|
|
|
|
|
{
|
|
|
|
|
read $fd, $data, $bs;
|
|
|
|
|
close $fd;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$data .= pack("C",int(rand(256))) for 1..$bs;
|
|
|
|
|
}
|
|
|
|
|
return $data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Нормализация одной url относительно другой
|
|
|
|
|
sub normalize_url ($$)
|
|
|
|
|
{
|
|
|
|
|
my ($base, $url) = @_;
|
|
|
|
|
return $url if $url =~ m%^[a-z]+://%iso;
|
|
|
|
|
if ($url =~ m%^/%so)
|
|
|
|
|
{
|
2010-05-10 02:35:11 +04:00
|
|
|
|
$base = $1 if $base =~ m%^([a-z]+://[^/]*)%iso;
|
2008-12-01 02:31:02 +03:00
|
|
|
|
}
|
|
|
|
|
elsif ($url =~ /^\?/so)
|
|
|
|
|
{
|
|
|
|
|
$base = $& if $base =~ m/^[^\?]*/so;
|
|
|
|
|
}
|
2010-05-10 02:35:11 +04:00
|
|
|
|
elsif ($url =~ s/^((\.\.\/)+)\/*//so)
|
|
|
|
|
{
|
|
|
|
|
my $n = length($1)/3;
|
|
|
|
|
my $d;
|
|
|
|
|
$base =~ m%^([a-z]+://[^/]*)/*(.*)$%iso;
|
|
|
|
|
($base, $d) = ($1, $2);
|
|
|
|
|
$d =~ s!(/+[^/]*){0,$n}$!!s;
|
|
|
|
|
$base .= '/';
|
|
|
|
|
$base .= "$d/" if $d;
|
|
|
|
|
}
|
2008-12-01 02:31:02 +03:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$base = $` if $base =~ m%[^\/]*$%so;
|
|
|
|
|
}
|
|
|
|
|
return $base.$url;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-12 03:18:51 +03:00
|
|
|
|
# uri_escape, автоматически дёргающий uri_escape_utf8 если текст is_utf8
|
2009-09-20 18:16:11 +04:00
|
|
|
|
# не вызывайте это напрямую! только при use VMX::Common qw(uri_escape_hacks);
|
2009-01-12 03:18:51 +03:00
|
|
|
|
sub uri_escape
|
|
|
|
|
{
|
|
|
|
|
if (Encode::is_utf8($_[0]))
|
|
|
|
|
{
|
|
|
|
|
my $text = shift;
|
|
|
|
|
Encode::_utf8_off($text);
|
|
|
|
|
return &$uri_escape_original($text, @_);
|
|
|
|
|
}
|
|
|
|
|
return &$uri_escape_original(@_);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-07 03:32:12 +03:00
|
|
|
|
# utf8_on для скаляра или рекурсивный для хешей/массивов
|
|
|
|
|
sub utf8on
|
|
|
|
|
{
|
|
|
|
|
if (ref($_[0]) && $_[0] =~ /HASH/so)
|
|
|
|
|
{
|
|
|
|
|
utf8on($_[0]->{$_}) for keys %{$_[0]};
|
|
|
|
|
}
|
|
|
|
|
elsif (ref($_[0]) && $_[0] =~ /ARRAY/so)
|
|
|
|
|
{
|
|
|
|
|
utf8on($_) for @{$_[0]};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Encode::_utf8_on($_[0]);
|
|
|
|
|
}
|
|
|
|
|
return $_[0];
|
|
|
|
|
}
|
2009-01-12 03:18:51 +03:00
|
|
|
|
|
2010-02-10 22:03:34 +03:00
|
|
|
|
# utf8_off для скаляра или рекурсивный для хешей/массивов
|
|
|
|
|
sub utf8off
|
|
|
|
|
{
|
|
|
|
|
if (ref($_[0]) && $_[0] =~ /HASH/so)
|
|
|
|
|
{
|
|
|
|
|
utf8off($_[0]->{$_}) for keys %{$_[0]};
|
|
|
|
|
}
|
|
|
|
|
elsif (ref($_[0]) && $_[0] =~ /ARRAY/so)
|
|
|
|
|
{
|
|
|
|
|
utf8off($_) for @{$_[0]};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Encode::_utf8_off($_[0]);
|
|
|
|
|
}
|
|
|
|
|
return $_[0];
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-14 03:27:15 +03:00
|
|
|
|
# преобразование mysql даты/времени в UNIX время
|
2009-09-20 18:16:11 +04:00
|
|
|
|
sub mysql2time
|
|
|
|
|
{
|
|
|
|
|
require POSIX;
|
|
|
|
|
$_[0] ? POSIX::mktime(mysqllocaltime(@_)) : 0
|
|
|
|
|
}
|
2009-02-14 03:27:15 +03:00
|
|
|
|
|
|
|
|
|
# и в struct tm
|
|
|
|
|
sub mysqllocaltime
|
|
|
|
|
{
|
|
|
|
|
my ($date, $time) = @_;
|
|
|
|
|
$time ||= '';
|
|
|
|
|
if ("$date $time" =~ /^(\d+)-(\d+)-(\d+)(?:\s+(\d+):(\d+):(\d+))?/so)
|
|
|
|
|
{
|
|
|
|
|
return (int($6), int($5), int($4), int($3), int($2)-1, int($1)-1900);
|
|
|
|
|
}
|
|
|
|
|
return ();
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-25 20:20:51 +03:00
|
|
|
|
# рекурсивная версия from_to
|
|
|
|
|
sub rfrom_to
|
|
|
|
|
{
|
|
|
|
|
if (ref($_[0]) && $_[0] =~ /HASH/so)
|
|
|
|
|
{
|
|
|
|
|
rfrom_to($_[0]->{$_}, $_[1], $_[2]) for keys %{$_[0]};
|
|
|
|
|
}
|
|
|
|
|
elsif (ref($_[0]) && $_[0] =~ /ARRAY/so)
|
|
|
|
|
{
|
|
|
|
|
rfrom_to($_, $_[1], $_[2]) for @{$_[0]};
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Encode::from_to($_[0], $_[1], $_[2]);
|
|
|
|
|
}
|
|
|
|
|
return $_[0];
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-09 17:42:29 +04:00
|
|
|
|
# s///, возвращающий значение...
|
|
|
|
|
# $1 $2 и т.п. в $replacement не работают
|
|
|
|
|
# resub($re, $replacement, $value)
|
|
|
|
|
sub resub
|
|
|
|
|
{
|
|
|
|
|
my ($re, $replacement, $value) = @_;
|
|
|
|
|
$re = qr/$re/s unless ref $re eq 'REGEXP';
|
|
|
|
|
$value =~ s/$re/$replacement/g;
|
|
|
|
|
return $value;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-27 17:45:22 +04:00
|
|
|
|
# \Q\E от $_[0]
|
|
|
|
|
sub requote
|
|
|
|
|
{
|
|
|
|
|
"\Q$_[0]\E";
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-11 02:27:34 +04:00
|
|
|
|
# недеструктивное объединение хешрефов
|
|
|
|
|
sub hashmrg
|
|
|
|
|
{
|
|
|
|
|
return undef unless @_;
|
|
|
|
|
my $h;
|
|
|
|
|
for (@_)
|
|
|
|
|
{
|
|
|
|
|
if ($_ && %$_)
|
|
|
|
|
{
|
|
|
|
|
if ($h)
|
|
|
|
|
{
|
|
|
|
|
$h = { %$h, %$_ };
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$h = $_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $h;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-02 21:32:06 +03:00
|
|
|
|
# AQG = 'Apostrophe', "Quote", `Grave Accent`
|
2009-06-02 17:37:26 +04:00
|
|
|
|
our $litsplit_AQG = qr/\'(?:[^\'\\]+|\\.)+\'|\"(?:[^\"\\]+|\\.)+\"|\`(?:[^\`\\]+|\\.)+\`/;
|
|
|
|
|
our $litsplit_AQ = qr/\'(?:[^\'\\]+|\\.)+\'|\"(?:[^\"\\]+|\\.)+\"/;
|
|
|
|
|
our $litsplit_QG = qr/\"(?:[^\"\\]+|\\.)+\"|\`(?:[^\`\\]+|\\.)+\`/;
|
|
|
|
|
our $litsplit_AG = qr/\'(?:[^\'\\]+|\\.)+\'|\`(?:[^\`\\]+|\\.)+\`/;
|
|
|
|
|
our $litsplit_A = qr/\'(?:[^\'\\]+|\\.)+\'/;
|
|
|
|
|
our $litsplit_Q = qr/\"(?:[^\"\\]+|\\.)+\"/;
|
|
|
|
|
our $litsplit_G = qr/\`(?:[^\`\\]+|\\.)+\`/;
|
|
|
|
|
|
2009-09-20 18:16:11 +04:00
|
|
|
|
my $litsplit_types = {
|
|
|
|
|
aqg => $litsplit_AQG,
|
|
|
|
|
agq => $litsplit_AQG,
|
|
|
|
|
qag => $litsplit_AQG,
|
|
|
|
|
qga => $litsplit_AQG,
|
|
|
|
|
gaq => $litsplit_AQG,
|
|
|
|
|
gqa => $litsplit_AQG,
|
|
|
|
|
aq => $litsplit_AQ,
|
|
|
|
|
qa => $litsplit_AQ,
|
|
|
|
|
gq => $litsplit_QG,
|
|
|
|
|
qg => $litsplit_QG,
|
|
|
|
|
ag => $litsplit_AG,
|
|
|
|
|
ga => $litsplit_AG,
|
|
|
|
|
a => $litsplit_A,
|
|
|
|
|
q => $litsplit_Q,
|
|
|
|
|
g => $litsplit_G,
|
|
|
|
|
};
|
|
|
|
|
|
2009-04-15 23:52:48 +04:00
|
|
|
|
# разбиение строки по регэкспу, однако не как split(//), а с учётом литералов,
|
|
|
|
|
# входящих в строку. границы литералов можно задавать доп.аргументом
|
2009-09-20 18:16:11 +04:00
|
|
|
|
# по умолчанию заключённые в 'одинарные', "двойные", или `обратные` кавычки строки.
|
2009-04-15 23:52:48 +04:00
|
|
|
|
# @a = litsplit /PATTERN/, EXPR[, LIMIT[, /LITERAL_PATTERN/]]
|
2009-09-20 18:16:11 +04:00
|
|
|
|
# LITERAL_PATTERN может быть равно сочетаниям букв "aqg"
|
2009-04-15 23:52:48 +04:00
|
|
|
|
sub litsplit
|
|
|
|
|
{
|
|
|
|
|
my ($re, $s, $lim, $lit) = @_;
|
2009-10-23 17:39:24 +04:00
|
|
|
|
$lit = $litsplit_types->{lc $$lit} if ref($lit) eq 'SCALAR';
|
2009-06-02 17:37:26 +04:00
|
|
|
|
$lit ||= $litsplit_AQG;
|
2009-04-15 23:52:48 +04:00
|
|
|
|
my @r;
|
|
|
|
|
my $l = 0;
|
|
|
|
|
my $ml;
|
|
|
|
|
$s =~ /^/g;
|
2009-06-16 20:25:29 +04:00
|
|
|
|
while ($s =~ /\G((?:$lit|.+?)*?)$re/gc && (!$lim || $lim <= 0 || @r+1 < $lim))
|
2009-04-15 23:52:48 +04:00
|
|
|
|
{
|
|
|
|
|
push @r, $1;
|
|
|
|
|
}
|
|
|
|
|
push @r, substr($s, pos($s));
|
|
|
|
|
return @r;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-24 03:14:22 +03:00
|
|
|
|
# ограничение длины строки $maxlen символами на границе пробелов и добавление '...', если что.
|
|
|
|
|
sub strlimit
|
|
|
|
|
{
|
2011-01-12 03:43:17 +03:00
|
|
|
|
my ($str, $maxlen, $dots) = @_;
|
2010-03-24 03:14:22 +03:00
|
|
|
|
if (!$maxlen || $maxlen < 1 || length($str) <= $maxlen)
|
|
|
|
|
{
|
|
|
|
|
return $str;
|
|
|
|
|
}
|
|
|
|
|
$str = substr($str, 0, $maxlen);
|
|
|
|
|
my $p = rindex($str, ' ');
|
|
|
|
|
if ($p < 0 || (my $pt = rindex($str, "\t")) > $p)
|
|
|
|
|
{
|
|
|
|
|
$p = $pt;
|
|
|
|
|
}
|
|
|
|
|
if ($p > 0)
|
|
|
|
|
{
|
|
|
|
|
# обрезаем
|
|
|
|
|
$str = substr($str, 0, $p);
|
|
|
|
|
}
|
2011-01-12 03:43:17 +03:00
|
|
|
|
return $str . (defined $dots ? $dots : '...');
|
2010-03-24 03:14:22 +03:00
|
|
|
|
}
|
|
|
|
|
|
2010-07-02 18:21:52 +04:00
|
|
|
|
# уход в подполье
|
|
|
|
|
sub daemonize
|
|
|
|
|
{
|
|
|
|
|
require POSIX;
|
|
|
|
|
my $logger;
|
|
|
|
|
if ($INC{'Log/Log4perl.pm'})
|
|
|
|
|
{
|
|
|
|
|
$logger = Log::Log4perl::get_logger();
|
|
|
|
|
}
|
|
|
|
|
$logger and $logger->info("[$$] Backgrounding");
|
|
|
|
|
my $pid = fork();
|
|
|
|
|
if (!defined $pid)
|
|
|
|
|
{
|
|
|
|
|
$logger and $logger->fatal("[$$] Bad Fork");
|
|
|
|
|
die "bad fork";
|
|
|
|
|
}
|
|
|
|
|
elsif ($pid)
|
|
|
|
|
{
|
|
|
|
|
$logger and $logger->info("[$$] Parent Exiting");
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
open STDIN, "/dev/null";
|
|
|
|
|
open STDOUT, ">/dev/null";
|
|
|
|
|
POSIX::setsid();
|
|
|
|
|
$logger and $logger->info("[$$] Child Running");
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-29 17:41:38 +04:00
|
|
|
|
# функция чтения CSV-файлов
|
|
|
|
|
# Multiline CSV compatible!
|
|
|
|
|
sub csv_read_record
|
|
|
|
|
{
|
|
|
|
|
my ($fh, $enc, $s, $q) = @_;
|
|
|
|
|
$q ||= '"';
|
|
|
|
|
$s ||= ',';
|
|
|
|
|
my $re_field = qr/^\s*(?:$q((?:[^$q]|$q$q)*)$q|([^$q$s]*))\s*($s)?/s;
|
|
|
|
|
my @parts = ();
|
|
|
|
|
my $line = "";
|
|
|
|
|
my $num_lines = 0;
|
|
|
|
|
my $l;
|
|
|
|
|
my $i;
|
|
|
|
|
while (<$fh>)
|
|
|
|
|
{
|
|
|
|
|
trick_taint($_);
|
|
|
|
|
$l = $_;
|
|
|
|
|
if ($enc && $enc ne 'utf-8')
|
|
|
|
|
{
|
|
|
|
|
Encode::from_to($l, $enc, 'utf-8');
|
|
|
|
|
}
|
|
|
|
|
Encode::_utf8_on($l);
|
|
|
|
|
$line .= $l;
|
|
|
|
|
while ($line =~ s/$re_field//)
|
|
|
|
|
{
|
2010-09-29 17:49:47 +04:00
|
|
|
|
$l = $1 || $2;
|
|
|
|
|
$l =~ s/$q$q/$q/gs;
|
|
|
|
|
push @parts, $l;
|
2010-09-29 17:41:38 +04:00
|
|
|
|
return \@parts if !$3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (length $line)
|
|
|
|
|
{
|
|
|
|
|
warn "eol before last field end\n";
|
|
|
|
|
warn "-->$line<--\n";
|
|
|
|
|
}
|
|
|
|
|
return @parts ? \@parts : undef;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-03 03:42:07 +03:00
|
|
|
|
# Экранирование кавычек в SQL/CSV-стиле (" -> "")
|
|
|
|
|
sub sql_quote
|
|
|
|
|
{
|
|
|
|
|
my ($a) = @_;
|
|
|
|
|
$a =~ s/\"/\"\"/gso;
|
|
|
|
|
return $a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# JSON-кодирование, автоматически подключает модуль JSON
|
|
|
|
|
sub encode_json
|
|
|
|
|
{
|
|
|
|
|
require JSON;
|
|
|
|
|
*encode_json = *JSON::encode_json;
|
|
|
|
|
goto &JSON::encode_json;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-21 03:34:53 +04:00
|
|
|
|
1;
|
2008-09-06 02:38:55 +04:00
|
|
|
|
__END__
|