File Coverage

blib/lib/URI/Encode.pm
Criterion Covered Total %
statement 67 71 94.3
branch 20 28 71.4
condition n/a
subroutine 14 14 100.0
pod 5 5 100.0
total 106 118 89.8


line stmt bran cond sub pod time code
1             package URI::Encode;
2              
3             #######################
4             # LOAD MODULES
5             #######################
6 2     2   36297 use strict;
  2         4  
  2         116  
7 2     2   11 use warnings FATAL => 'all';
  2         3  
  2         93  
8              
9 2     2   53 use 5.008001;
  2         4  
  2         74  
10 2     2   1870 use Encode qw();
  2         10239  
  2         66  
11 2     2   14 use Carp qw(croak carp);
  2         3  
  2         219  
12              
13             #######################
14             # VERSION
15             #######################
16             our $VERSION = '1.0.1';
17              
18             #######################
19             # EXPORT
20             #######################
21 2     2   14 use base qw(Exporter);
  2         3  
  2         2440  
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 98 my ( $class, @in ) = @_;
45              
46             # Check Input
47 6         25 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         15 my $input = {};
59 6 50       23 if ( ref $in[0] eq 'HASH' ) { $input = $in[0]; }
  0         0  
60 6         15 else { $input = {@in}; }
61              
62             # Set options
63 6         58 my $options = {
64              
65             # Defaults
66 6         36 %{$defaults},
67              
68             # Input
69 1536         4743 %{$input},
70              
71             # Encoding Map
72             enc_map =>
73 1536         4577 { ( map { chr($_) => sprintf( "%%%02X", $_ ) } ( 0 ... 255 ) ) },
74              
75             # Decoding Map
76             dec_map =>
77 6         14 { ( map { sprintf( "%02X", $_ ) => chr($_) } ( 0 ... 255 ) ), },
78             };
79              
80             # Return
81 6         340 my $self = bless $options, $class;
82 6         56 return $self;
83             } ## end sub new
84              
85             #######################
86             # ENCODE
87             #######################
88             sub encode {
89 9     9 1 1587 my ( $self, $data, $options ) = @_;
90              
91             # Check for data
92             # Allow to be '0'
93 9 50       34 return unless defined $data;
94              
95 9         26 my $enc_res = $self->{encode_reserved};
96 9         15 my $double_encode = $self->{double_encode};
97              
98 9 100       33 if ( defined $options ) {
99 7 100       29 if ( ref $options eq 'HASH' ) {
100 5 100       19 $enc_res = $options->{encode_reserved}
101             if exists $options->{encode_reserved};
102 5 100       18 $double_encode = $options->{double_encode}
103             if exists $options->{double_encode};
104             } ## end if ( ref $options eq 'HASH')
105             else {
106 2         5 $enc_res = $options;
107             }
108             } ## end if ( defined $options )
109              
110             # UTF-8 encode
111 9         37 $data = Encode::encode( 'utf-8-strict', $data );
112              
113             # Encode a literal '%'
114 9 100       322 if ($double_encode) { $data =~ s{(\%)}{$self->_get_encoded_char($1)}gex; }
  7         51  
  7         26  
115 2         21 else { $data =~ s{(\%)}{$self->_encode_literal_percent($1, $')}gex; }
  34         100  
116              
117             # Percent Encode
118 9 100       25 if ($enc_res) {
119 4         35 $data =~ s{$unreserved_re}{$self->_get_encoded_char($1)}gex;
  64         107  
120             }
121             else {
122 5         46 $data =~ s{$reserved_re}{$self->_get_encoded_char($1)}gex;
  14         28  
123             }
124              
125             # Done
126 9         71 return $data;
127             } ## end sub encode
128              
129             #######################
130             # DECODE
131             #######################
132             sub decode {
133 4     4 1 1075 my ( $self, $data ) = @_;
134              
135             # Check for data
136             # Allow to be '0'
137 4 50       17 return unless defined $data;
138              
139             # Percent Decode
140 4         51 $data =~ s{$encoded_chars}{ $self->_get_decoded_char($1) }gex;
  14         33  
141              
142 4         26 return $data;
143             } ## end sub decode
144              
145             #######################
146             # EXPORTED FUNCTIONS
147             #######################
148              
149             # Encoder
150 4     4 1 1256 sub uri_encode { return __PACKAGE__->new()->encode(@_); }
151              
152             # Decoder
153 1     1 1 7 sub uri_decode { return __PACKAGE__->new()->decode(@_); }
154              
155             #######################
156             # INTERNAL
157             #######################
158              
159              
160             sub _get_encoded_char {
161 85     85   128 my ( $self, $char ) = @_;
162 85 50       439 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 34     34   78 my ( $self, $char, $post ) = @_;
169 34 50       64 return $self->_get_encoded_char($char) if not defined $post;
170 34 50       109 if ( $post =~ m{^([a-fA-F0-9]{2})}x ) {
171 34 50       97 return $self->_get_encoded_char($char)
172             unless exists $self->{dec_map}->{$1};
173 34         133 return $char;
174             } ## end if ( $post =~ m{^([a-fA-F0-9]{2})}x)
175 0         0 return $self->_get_encoded_char($char);
176             } ## end sub _encode_literal_percent
177              
178              
179             sub _get_decoded_char {
180 14     14   24 my ( $self, $char ) = @_;
181 14 50       94 return $self->{dec_map}->{ uc($char) }
182             if exists $self->{dec_map}->{ uc($char) };
183 0           return $char;
184             } ## end sub _get_decoded_char
185              
186             #######################
187             1;
188              
189             __END__