File Coverage

blib/lib/URI/Encode.pm
Criterion Covered Total %
statement 69 72 95.8
branch 20 28 71.4
condition 1 3 33.3
subroutine 14 14 100.0
pod 5 5 100.0
total 109 122 89.3


line stmt bran cond sub pod time code
1             package URI::Encode;
2              
3             #######################
4             # LOAD MODULES
5             #######################
6 2     2   21753 use strict;
  2         3  
  2         64  
7 2     2   8 use warnings FATAL => 'all';
  2         3  
  2         80  
8              
9 2     2   54 use 5.008001;
  2         5  
10 2     2   515 use Encode qw();
  2         6550  
  2         54  
11 2     2   8 use Carp qw(croak carp);
  2         3  
  2         125  
12              
13             #######################
14             # VERSION
15             #######################
16             our $VERSION = '1.1.1';
17              
18             #######################
19             # EXPORT
20             #######################
21 2     2   6 use base qw(Exporter);
  2         2  
  2         1426  
22             our (@EXPORT_OK);
23              
24             @EXPORT_OK = qw(uri_encode uri_decode);
25              
26             #######################
27             # SETTINGS
28             #######################
29              
30             # Reserved characters
31             my $reserved_re
32             = qr{([^a-zA-Z0-9\-\_\.\~\!\*\'\(\)\;\:\@\&\=\+\$\,\/\?\#\[\]\%])}x;
33              
34             # Un-reserved characters
35             my $unreserved_re = qr{([^a-zA-Z0-9\Q-_.~\E\%])}x;
36              
37             # Encoded character set
38             my $encoded_chars = qr{%([a-fA-F0-9]{2})}x;
39              
40             #######################
41             # CONSTRUCTOR
42             #######################
43             sub new {
44 6     6 1 53 my ( $class, @in ) = @_;
45              
46             # Check Input
47 6         14 my $defaults = {
48              
49             # this module, unlike URI::Escape,
50             # does not encode reserved characters
51             encode_reserved => 0,
52              
53             # Allow Double encoding?
54             # defaults to YES
55             double_encode => 1,
56             };
57              
58 6         6 my $input = {};
59 6 50       11 if ( ref $in[0] eq 'HASH' ) { $input = $in[0]; }
  0         0  
60 6         7 else { $input = {@in}; }
61              
62             # Set options
63             my $options = {
64              
65             # Defaults
66 6         14 %{$defaults},
67              
68             # Input
69 6         13 %{$input},
70              
71             # Encoding Map
72             enc_map =>
73 1536         2114 { ( map { chr($_) => sprintf( "%%%02X", $_ ) } ( 0 ... 255 ) ) },
74              
75             # Decoding Map
76             dec_map =>
77 6         6 { ( map { sprintf( "%02X", $_ ) => chr($_) } ( 0 ... 255 ) ), },
  1536         1938  
78             };
79              
80             # Return
81 6         178 my $self = bless $options, $class;
82 6         24 return $self;
83             } ## end sub new
84              
85             #######################
86             # ENCODE
87             #######################
88             sub encode {
89 9     9 1 533 my ( $self, $data, $options ) = @_;
90              
91             # Check for data
92             # Allow to be '0'
93 9 50       17 return unless defined $data;
94              
95 9         12 my $enc_res = $self->{encode_reserved};
96 9         6 my $double_encode = $self->{double_encode};
97              
98 9 100       16 if ( defined $options ) {
99 7 100       14 if ( ref $options eq 'HASH' ) {
100             $enc_res = $options->{encode_reserved}
101 5 100       9 if exists $options->{encode_reserved};
102             $double_encode = $options->{double_encode}
103 5 100       10 if exists $options->{double_encode};
104             } ## end if ( ref $options eq 'HASH')
105             else {
106 2         3 $enc_res = $options;
107             }
108             } ## end if ( defined $options )
109              
110             # UTF-8 encode
111 9         20 $data = Encode::encode( 'utf-8-strict', $data );
112              
113             # Encode a literal '%'
114 9 100       181 if ($double_encode) { $data =~ s{(\%)}{$self->_get_encoded_char($1)}gex; }
  7         30  
  7         12  
115 2         9 else { $data =~ s{(\%)(.*)}{$self->_encode_literal_percent($1, $2)}gex; }
  2         5  
116              
117             # Percent Encode
118 9 100       14 if ($enc_res) {
119 4         15 $data =~ s{$unreserved_re}{$self->_get_encoded_char($1)}gex;
  64         60  
120             }
121             else {
122 5         24 $data =~ s{$reserved_re}{$self->_get_encoded_char($1)}gex;
  14         18  
123             }
124              
125             # Done
126 9         36 return $data;
127             } ## end sub encode
128              
129             #######################
130             # DECODE
131             #######################
132             sub decode {
133 4     4 1 318 my ( $self, $data ) = @_;
134              
135             # Check for data
136             # Allow to be '0'
137 4 50       10 return unless defined $data;
138              
139             # Percent Decode
140 4         27 $data =~ s{$encoded_chars}{ $self->_get_decoded_char($1) }gex;
  14         21  
141              
142 4         17 return $data;
143             } ## end sub decode
144              
145             #######################
146             # EXPORTED FUNCTIONS
147             #######################
148              
149             # Encoder
150 4     4 1 533 sub uri_encode { return __PACKAGE__->new()->encode(@_); }
151              
152             # Decoder
153 1     1 1 2 sub uri_decode { return __PACKAGE__->new()->decode(@_); }
154              
155             #######################
156             # INTERNAL
157             #######################
158              
159              
160             sub _get_encoded_char {
161 85     85   77 my ( $self, $char ) = @_;
162 85 50       223 return $self->{enc_map}->{$char} if exists $self->{enc_map}->{$char};
163 0         0 return $char;
164             } ## end sub _get_encoded_char
165              
166              
167             sub _encode_literal_percent {
168 2     2   5 my ( $self, $char, $post ) = @_;
169              
170 2 50       5 return $self->_get_encoded_char($char) if not defined $post;
171              
172 2         1 my $return_char;
173 2 50       7 if ( $post =~ m{^([a-fA-F0-9]{2})}x ) {
174 2 50       5 if ( exists $self->{dec_map}->{$1} ) {
175 2         6 $return_char = join( '', $char, $post );
176             }
177             } ## end if ( $post =~ m{^([a-fA-F0-9]{2})}x)
178              
179 2   33     4 $return_char ||= join( '', $self->_get_encoded_char($char), $post );
180 2         7 return $return_char;
181             } ## end sub _encode_literal_percent
182              
183              
184             sub _get_decoded_char {
185 14     14   20 my ( $self, $char ) = @_;
186             return $self->{dec_map}->{ uc($char) }
187 14 50       61 if exists $self->{dec_map}->{ uc($char) };
188 0           return $char;
189             } ## end sub _get_decoded_char
190              
191             #######################
192             1;
193              
194             __END__