File Coverage

blib/lib/JSONP.pm
Criterion Covered Total %
statement 97 417 23.2
branch 23 222 10.3
condition 18 149 12.0
subroutine 25 52 48.0
pod 23 24 95.8
total 186 864 21.5


line stmt bran cond sub pod time code
1             package JSONP;
2             # some older 5.8.x perl versions on exotic platforms don't get the v5.10 syntax
3 1     1   72787 use 5.010;
  1         5  
4 1     1   9 use v5.10;
  1         3  
5 1     1   7 use strict;
  1         2  
  1         23  
6 1     1   4 use warnings;
  1         2  
  1         37  
7 1     1   686 use utf8;
  1         15  
  1         5  
8 1     1   575 use Time::HiRes qw(gettimeofday);
  1         1510  
  1         4  
9 1     1   998 use File::Temp qw();
  1         22289  
  1         33  
10 1     1   9 use File::Path;
  1         2  
  1         63  
11 1     1   612 use Encode;
  1         10883  
  1         76  
12 1     1   18 use Cwd qw();
  1         2  
  1         21  
13 1     1   5 use Scalar::Util qw(reftype);
  1         2  
  1         48  
14 1     1   1009 use CGI qw();
  1         32735  
  1         36  
15 1     1   717 use Digest::SHA;
  1         3346  
  1         49  
16 1     1   719 use JSON;
  1         10787  
  1         8  
17 1     1   787 use Want;
  1         1570  
  1         227  
18              
19             our $VERSION = '2.19';
20              
21             =encoding utf8
22              
23             =head1 NAME
24              
25             JSONP - a module to quickly build JSON/JSONP web services, providing also some syntactic sugar acting a bit like a sort of DSL (domain specific language) for JSON.
26              
27             =head1 SYNOPSIS
28              
29             =over 2
30              
31             =item * under CGI environment:
32              
33             You can pass the name of instance variable, skipping the I<-Enew> call.
34             If you prefer, you can use I<-Enew> just passing nothing in I.
35              
36             use JSONP 'jsonp';
37             $jsonp->run;
38              
39             ...
40              
41             sub yoursubname
42             {
43             $j->table->fields = $sh->{NAME};
44             $j->table->data = $sh->fetchall_arrayref;
45             }
46              
47             OR
48              
49             use JSONP;
50              
51             my $j = JSONP->new;
52             $j->run;
53              
54             ...
55              
56             sub yoursubname
57             {
58             $j->table->fields = $sh->{NAME};
59             $j->table->data = $sh->fetchall_arrayref;
60             }
61              
62             =item * under mod_perl:
63              
64             You must declare the instance variable, remember to use I.
65              
66             use JSONP;
67             local our $j = JSONP->new;
68             $j->run;
69              
70             ...
71              
72             sub yoursubname
73             {
74             my $namedparam = $j->params->namedparam;
75             $j->table->fields = $sh->{NAME};
76             $j->table->data = $sh->fetchall_arrayref;
77             }
78              
79             option setting methods allow for chained calls:
80              
81             use JSONP;
82             local our $j = JSONP->new;
83             $j->aaa('your_session_sub')->login('your_login_sub')->debug->insecure->run;
84              
85             ...
86              
87             sub yoursubname
88             {
89             my $namedparam = $j->params->namedparam;
90             $j->table->fields = $sh->{NAME};
91             $j->table->data = $sh->fetchall_arrayref;
92             }
93              
94             just make sure I it is the last element in chain.
95              
96             =back
97              
98             the module will call automatically the sub which name is specified in the req parameter of GET/POST request. JSONP will check if the sub exists in current script namespace by looking in typeglob and only in that case the sub will be called. The built-in policy about function names requires also a name starting by a lowercase letter, followed by up to 63 characters chosen between ASCII letters, numbers, and underscores. Since this module is intended to be used by AJAX calls, this will spare you to define routes and mappings between requests and back end code. In your subroutines you will therefore add all the data you want to the JSON/JSONP object instance in form of hashmap of any deep and complexity, JSONP will return that data automatically as JSON object with/without padding (by using the function name passed as 'callback' in GET/POST request, or using simply 'callback' as default) to the calling javascript. The supplied callback name wanted from calling javascript must follow same naming conventions as function names above. Please note that I and I keys on top of JSONP object hierarchy are reserved. See also "I" paragraph at the end of the POD.
99             The jQuery call:
100              
101             // note that jQuery will automatically chose a non-clashing callback name when you insert callback=? in request
102             $.getJSON(yourwebserverhost + '?req=yoursubname&firstparam=firstvalue&...&callback=?', function(data){
103             //your callback code
104             });
105              
106             processed by JSONP, will execute I in your script if it exists, otherwise will return a JSONP codified error. The default error object returned by this module in its root level has a boolean "error" flag and an "errors" array where you can put a list of your customized errors. The structure of the elements of the array is of course free so you can adapt it to your needs and frameworks.
107              
108             you can autovivify the response hash omiting braces
109              
110             $jsonp->firstlevelhashvalue = 'I am a first level hash value';
111             $jsonp->first->second = 'I am a second level hash value';
112              
113             you can then access hash values either with or without braces notation
114              
115             $jsonp->firstlevelhashvalue = 5;
116             print $jsonp->firstlevelhashvalue; # will print 5
117              
118             it is equivalent to:
119              
120             $jsonp->{firstlevelhashvalue} = 5;
121             print $jsonp->{firstlevelhashvalue};
122              
123             you can even build a tree:
124              
125             $jsonp->first->second = 'hello!';
126             print $jsonp->first->second; # will print "hello!"
127              
128             it is the same as:
129              
130             $jsonp->{first}->{second} = 'hello!';
131             print $jsonp->{first}->{second};
132              
133             or (the perl "array rule"):
134              
135             $jsonp->{first}{second} = 'hello!';
136             print $jsonp->{first}{second};
137              
138             or even (deference ref):
139              
140             $$jsonp{first}{second} = 'hello!';
141             print $$jsonp{first}{second};
142              
143             you can insert hashes at any level of structure and they will become callable with the built-in convenience shortcut:
144              
145             my $obj = {a => 1, b => 2};
146             $jsonp->first->second = $obj;
147             print $jsonp->first->second->b; # will print 2
148             $jsonp->first->second->b = 3;
149             print $jsonp->first->second->b; # will print 3
150              
151             you can insert also array at any level of structure and the nodes (hashrefs) within resulting structure will become callable with the built-in convenience shortcut. You will need to call C<-E[index]> in order to access them, though:
152              
153             my $ary = [{a => 1}, 2];
154             $jsonp->first->second = $ary;
155             print $jsonp->first->second->[1]; # will print 2
156             print $jsonp->first->second->[0]->a; # will print 1
157             $jsonp->first->second->[0]->a = 9;
158             print $jsonp->first->second->[0]->a; # will print 9 now
159              
160             you can almost freely interleave above listed styles in order to access to elements of JSONP object. As usual, respect I<_private> variables if you don't know what you are doing. One value-leaf/object-node element set by the convenience notation shortcut will be read by normal hash access syntax. You can delete elements from the hash tree, though it is not supported via the convenience notation. You can use it, but the last node has to be referenced via braces notation:
161              
162             my $j = JSONP->new;
163             $j->firstnode->a = 5;
164             $j->firstnode->b = 9;
165             $j->secondnode->thirdnode->a = 7;
166             delete $j->secondnode->{thirdnode}; # will delete thirdnode as expected in hash structures.
167              
168             TODO: will investigate if possible to implement deletion using exclusively the convenience notation feature.
169              
170             IMPORTANT NOTE: while using the convenience notation without braces, if you autovivify a hierarchy without assigning anything to the last item, or assigning it an B>ined value, JSONP will assign to the last element a zero string ( '' ). Since it evaluates to false in a boolean context and can be safely catenated to other strings without causing runtime errors you can avoid several I checks without the risk to incur in runtime errors. The only dangerous tree traversal can occur if you try to treat an object node as an array node, or vice versa.
171              
172             IMPORTANT NOTE 2: remember that all the method names of the module cannot be used as key names via convenience notation feature, at any level of the response tree. You can set such key names anyway by using the braces notation. To retrieve their value, you will need to use the brace notation for the node that has the key equal to a native method name of this very module. It is advisable to assign the branch that contains them to an higher level node:
173              
174             my $j = JSONP->new;
175             $j->firstnode = 5;
176             my $branch = {};
177             $branch->{debug} = 0; # debug is a native method name
178             $branch->{serialize} = 1; # serialize is a native method name
179             $j->secondnode = $branch; # $branch structure will be grafted and relative nodes blessed accordingly
180             say $j->secondnode->{serialize}; # will print 1
181              
182             IMPORTANT NOTE 3: deserialized booleans from JSON are turned into referenes to scalars by JSON module, to say JSON I will turn into a Perl I<\1> and JSON I will turn into a Perl I<\0>. JSONP module detects boolen context so when you try to evaluate one of these values in a boolean context it correctly returns the actual boolean value hold by the leaf instead of the reference (that would always evaluate to I even for I<\0>), to say will dereference I<\0> and I<\1> in order to return I<0> and I<1> respectively.
183              
184             $j->graft('testbool', q|{"true": true, "false":false}|);
185             say $j->testbool->true;
186             say $j->testbool->false;
187             say !! $j->testbool->true;
188             say !! $j->testbool->false;
189              
190             NOTE: in order to get a "pretty print" via serialize method you will need to either call I or I methods before serialize, use I if you want to serialize a deeper branch than the root one:
191              
192             my $j = JSONP->new->debug;
193             $j->firstnode->a = 5;
194             $j->firstnode->b = 9;
195             $j->secondnode->thirdnode->a = 7;
196             my $pretty = $j->serialize; # will get a pretty print
197             my $deepser = $j->firstnode->serialize; # won't get a pretty print, because deeper than root
198             my $prettydeeper = $j->firstnode->pretty->serialize; # will get a pretty print, because we called I first
199              
200             =head1 DESCRIPTION
201              
202             The purpose of JSONP is to give an easy and fast way to build JSON-only web services that can be used even from a different domain from which one they are hosted on. It is supplied only the object interface: this module does not export any symbol, apart the optional pointer to its own instance in the CGI environment (not possible in mod_perl environment).
203             Once you have the instance of JSONP, you can build a response hash tree, containing whatever data structure, that will be automatically sent back as JSON object to the calling page. The built-in automatic cookie session keeping uses a secure SHA256 to build the session key. The related cookie is HttpOnly, Secure (only SSL) and with path set way down the one of current script (keep the authentication script in the root of your scripts path to share session among all scripts). For high trusted intranet environments a method to disable the Secure flag has been supplied. The automatically built cookie key will be long exactly 64 chars (hex format).
204             You can retrieve parameters supplied from browser either via GET, POST, PUT, or DELETE by accessing the reserved I key of JSONP object. For example the value of a parameter named I will be accessed via $j->params->test. In case of POSTs or PUTs of application/json requests (JSONP application/javascript requests are always loaded as GETs) the JSONP module will transparently detect them and populate the I key with the deserialization of posted JSON, note that in this case the JSON being P(OS|U)Ted must be an object and not an array, having a I param key on the first level of the structure in order to point out the corresponding function to be invoked.
205             You have to provide the string name or sub ref (the module accepts either way) of your own I and I functions. The AAA (aaa) function will get called upon every request with the session key (retrieved from session cookie or newly created for brand new sessions) as argument. That way you will be free to implement routines for authentication, authorization, access, and session tracking that most suit your needs, together with rules for user/groups to access the methods you expose. Your AAA function must return the session string (if you previously saved it, read on) if a valid session exists under the given key. A return value evaluated as false by perl will result in a 'forbidden' response (you can add as much errors as you want in the I array of response object). B otherwise you will give access to all users. If you want you can check the invoked method under the req parameter (see query method) in order to implement your own access policies. B the request has been B (B)The AAA function will be called a second time just before the response to client will be sent out, the module checks for changes in session by concurrent requests that would have executed in meanwhile, and merges their changes with current one by a smart recursive data structure merge routine. Then it will call the AAA function again with the session key as first argument, and a serialized string of the B branch as second (as you would have modified it inside your called function). This way if your AAA function gets called with only one paramenter it is the begin of the request cycle, and you have to retrieve and check the session saved in your storage of chose (memcached, database, whatever), if it gets called with two arguments you can save the updated session object (already serialized as JSON) to the storage under the given key. The B key of JSONP object will be reserved for session tracking, everything you will save in that branch will be passed serialized to your AAA function right before the response to client. It will be also populated after the serialized string you will return from your AAA function at the beginning of the request cycle. The login function will get called with the current session key (from cookie or newly created) as parameter, you can retrieve the username and password passed by the query method, as all other parameters. This way you will be free to give whatever name you like to those two parameters. Return the outcome of login attempt in order to pass back to login javascript call the state of authentication. Whatever value that evaluates to true will be seen as "authentication ok", whatever value that Perl evaluates to false will be seen as "authentication failed". Subsequent calls (after authentication) will track the authentication status by mean of the session string you return from AAA function.
206             If you need to add a method/call/feature to your application you have only to add a sub with same name you will pass under I parameter from frontend.
207              
208             =head2 METHODS
209              
210             =cut
211              
212             sub import {
213 1     1   17 my ($self, $name) = @_;
214 1 50       6 return if $ENV{MOD_PERL};
215 1 50       15 return unless $name;
216 0 0       0 die 'not valid variable name' unless $name =~ /^[a-z][0-9a-zA-Z_]{0,63}$/;
217 0         0 my $symbol = caller() . '::' . $name;
218             {
219 1     1   8 no strict 'refs'; ## no critic
  1         2  
  1         1108  
  0         0  
220 0         0 *$symbol = \JSONP->new;
221             }
222             }
223              
224             =head3 new
225              
226             class constructor. The options have to be set by calling correspondant methods (see below). You can pass a Perl object reference (hash or array) or a JSON string to the constructor, and it will populate automatically the objext, note that when you are using the object as a manager for a web service, .
227              
228             my $h = {
229             a => 1,
230             b => 2
231             }:
232             my $j = JSONP->new($h);
233             say $j->serialize;
234              
235             my $a = ['a', 'b', 'c'];
236             my $j = JSONP->new($a);
237             say $j->serialize;
238              
239             my $json = '{"a" : 1, "b" : 2}';
240             my $j = JSONP->new($json);
241             say $j->serialize;
242              
243             =cut
244              
245             sub new {
246 1     1 1 93 my ($class, $json) = @_;
247              
248 1 50       6 return bless {}, $class unless defined $json;
249              
250 0   0     0 my $type = reftype($json) // '';
251 0 0 0     0 if ($type eq 'HASH' || $type eq 'ARRAY') {
252             # shallow blessing to avoid constructor overhead on large data structures
253             # on-fly blessing is performed on dynamic traversal in AUTOLOAD and loop
254 0         0 return bless $json, $class;
255             }
256              
257 0 0       0 if ($type eq '') {
258 0         0 eval{
259 0         0 local $SIG{'__DIE__'};
260 0   0     0 $json = JSON->new->decode($json // '');
261             };
262              
263 0 0       0 unless($@) {
264             # shallow blessing to avoid constructor overhead on large data structures
265             # on-fly blessing is performed on dynamic traversal in AUTOLOAD and loop
266 0         0 return bless $json, $class;
267             }
268             }
269              
270 0         0 return 0;
271             }
272              
273             =head3 run
274              
275             executes the subroutine specified by req paramenter, if it exists, and returns the JSON output object to the calling browser. This have to be the last method called from JSONP object, because it will call the requested function and return the set object as JSON one.
276              
277             =cut
278              
279             sub _auth {
280 0     0   0 my ($self, $sid, $session) = @_;
281 0         0 my $authenticated = eval {
282 0         0 local $SIG{'__DIE__'};
283 0         0 $self->{_aaa_sub}->($sid, $session);
284             };
285              
286 0 0       0 if($@){
287 0 0       0 $self->{eval} = $@ if $self->{_debug};
288 0         0 $self->raiseError('unclassified error');
289 0         0 $authenticated = 0;
290             }
291              
292 0         0 $authenticated;
293             }
294              
295             sub run {
296 0     0 1 0 my $self = shift;
297 0         0 $self->{_is_root_element} = 1;
298 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
299 0         0 $self->{_authenticated} = 0;
300 0         0 $self->{error} = \0;
301 0         0 $self->errors = [];
302 0         0 $self->{_passthrough} = 0;
303 0         0 $self->{_mimetype} = 'text/html';
304 0         0 $self->{_html} = 0;
305 0         0 $self->{_mod_perl} = defined $ENV{MOD_PERL};
306 0         0 $self->{_jsonp_version} = $VERSION;
307             # File::Temp will remove the tempdir and its content on after request end
308 0         0 $self->{_tempdir} = File::Temp->newdir;
309 0         0 my $curdir = Cwd::cwd;
310             # Taint mode
311 0 0       0 $curdir = $curdir =~ m{(/.*)} ? $1 : '';
312 0         0 $self->{_curdir} = $curdir;
313             #$ENV{PATH} = '' if $self->{_taint_mode} = ${^TAINT};
314 0 0       0 die "you have to provide an AAA function" unless $self->{_aaa_sub};
315 0         0 my $r = CGI->new;
316             # this will enable us to give back the unblessed reference
317 0         0 my %params = $r->Vars;
318             # we assume all inputs are UTF-8, (XHR default encoding anyway) but check if params are already decoded for safety
319 0         0 for (keys %params) {
320 0 0       0 next if utf8::is_utf8($params{$_});
321             # be wary on input UTF-8 format (use strict UTF-8 mode, not loose utf8)
322 0         0 $params{$_} = Encode::decode('UTF-8', $params{$_});
323             }
324 0   0     0 my $contype = $r->content_type // '';
325 0         0 my $method = $r->request_method;
326 0         0 $self->{_request_method} = $method;
327 0 0 0     0 if($contype =~ m{application/json} && scalar keys %params == 1){
328 0         0 my $payload;
329 0 0       0 if($method eq 'POST'){
    0          
330 0         0 $payload = $params{POSTDATA};
331             } elsif ($method eq 'PUT'){
332 0         0 $payload = $params{PUTDATA};
333             } else {
334 0         0 $payload = '{}'; # dummy one, fallback for invalid requests
335             }
336              
337 0         0 my $success = $self->graft('params', $payload);
338              
339 0 0       0 unless($success){
340 0         0 $self->raiseError('invalid input JSON');
341             }
342              
343             } else {
344 0         0 $self->params = \%params;
345             }
346              
347 0 0 0     0 unless((reftype $self->params // '') eq 'HASH'){
348 0         0 $self->params = {};
349 0         0 $self->raiseError('invalid input JSON type (array)');
350             }
351              
352 0 0       0 if($self->{_rest}){
353 0         0 my $name = $0;
354 0         0 $name =~ m{([^/]+)$};
355 0   0     0 $name = $1 // '';
356 0         0 $self->{params}->{req} = $name;
357             }
358              
359 0   0     0 my $req = $self->{params}->{req} // '';
360 0   0     0 $req =~ /^([a-z][0-9a-zA-Z_]{1,63})$/; $req = $1 // '';
  0         0  
361 0         0 my $sid = $r->cookie('sid');
362              
363 0         0 my $map = caller() . '::' . $req;
364 0         0 my $session = $self->_auth($sid);
365 0         0 $self->{_authenticated} = ! ! $session;
366 0 0       0 if($self->{_authenticated}){
367 0 0       0 $self->session = {} unless $self->graft('session', $session);
368             } else {
369 0         0 $self->session = {};
370             }
371              
372 0         0 $$self{_cgi} = $r;
373              
374 0         0 my $isloginsub = \&$map == $self->{_login_sub};
375              
376 0         0 my $header = {-type => 'application/javascript', -charset => 'UTF-8'};
377 0 0 0     0 unless ( $sid && !$isloginsub) {
378 0         0 my $h = Digest::SHA->new(256);
379 0         0 my @us = gettimeofday;
380 0 0       0 $h->add(@us, map($r->http($_) , $r->http() )) if $self->{_insecure_session};
381 0 0       0 $h->add(@us, map($r->https($_), $r->https())) unless $self->{_insecure_session};
382 0         0 $sid = $h->hexdigest;
383 0         0 my $current_path = $r->url(-absolute=>1);
384 0         0 $current_path =~ s|/[^/]*$||;
385             my $cookie = {
386             -name => 'sid',
387             -value => $sid,
388             -path => $current_path,
389             -secure => !$self->{_insecure_session},
390 0         0 -httponly => 1,
391             # TODO test SameSite on IE11 on Windows 8 and Safari on MacOS X
392             #-samesite => 'Strict'
393             };
394 0 0       0 $cookie->{-expires} = "+$$self{_session_expiration}s" if $self->{_session_expiration};
395 0         0 $header->{-cookie} = $r->cookie($cookie);
396             }
397              
398 0 0 0     0 if (! ! $session && defined &$map || $isloginsub) {
    0 0        
399 0         0 eval {
400 0         0 local $SIG{'__DIE__'};
401 1     1   10 no strict 'refs'; ## no critic
  1         2  
  1         1731  
402 0         0 my $outcome = &$map($sid);
403 0 0       0 $self->{_authenticated} = $outcome if $isloginsub;
404             };
405              
406 0 0       0 if($@){
407 0 0       0 $self->{eval} = $@ if $self->{_debug};
408 0         0 $self->raiseError('unclassified error');
409             }
410              
411             # save back the session only during responses to PUT and POST HTTP methods
412 0 0 0     0 if($self->{_authenticated} && ($method eq 'POST' || $method eq 'PUT')){
      0        
413             # get session last changes made by concurrent requests
414             # and merge them with current session right before to
415             # pass it back to aaa sub that will save it to storage
416             # note that current session keys/values will override
417             # concurrent ones, see _merge function for details
418 0         0 my $concurrentSession = $self->_auth($sid);
419 0         0 my $thisSession = $self->session->serialize;
420 0         0 $self->graft('thisSession', $thisSession);
421 0         0 delete $self->{session};
422 0         0 $self->graft('session', $concurrentSession);
423 0         0 $self->_merge($self->session, $self->thisSession);
424 0         0 delete $self->{thisSession};
425 0         0 $self->_auth($sid, $self->session->serialize);
426             }
427              
428             } elsif (! $req) {
429 0         0 $self->raiseError('invalid request');
430             } else {
431 0         0 $self->raiseError('forbidden');
432             }
433              
434             # give a nice JSON "true"/"false" output for authentication
435 0 0       0 $self->authenticated = $self->{_authenticated} ? \1 : \0;
436 0   0     0 $header->{'-status'} = $self->{_status_code} || 200;
437 0         0 my $callback;
438              
439             # debug
440             # my @layers = PerlIO::get_layers(select);
441              
442 0         0 my $ofh = select;
443             # avoid putting multiple encoding layers on STDOUT
444 0 0       0 binmode($ofh) && binmode($ofh, ':utf8');
445 0 0       0 unless($self->{_passthrough}){
446 0 0       0 $callback = $self->params->callback if $self->{_request_method} eq 'GET';
447 0 0       0 if($callback){
448 0 0       0 $callback = $callback =~ /^([a-z][0-9a-zA-Z_]{1,63})$/ ? $1 : '';
449 0 0       0 $self->raiseError('invalid callback') unless $callback;
450             }
451              
452 0 0       0 $self->{_mimetype} = $callback ? 'application/javascript' : 'application/json';
453 0         0 $header->{'-type'} = $self->{_mimetype};
454 0         0 print $r->header($header);
455 0 0       0 print "$callback(" if $callback;
456 0         0 print $self->serialize;
457 0 0       0 print ')' if $callback;
458             } else {
459 0         0 $header->{'-type'} = $self->{_mimetype};
460 0 0       0 if($self->{_html}){
461 0         0 print $r->header($header);
462 0         0 print $self->{_html};
463             } else {
464 0 0       0 if ($self->{_inline}) {
465 0         0 $header->{'-disposition'} = 'inline';
466             } else {
467 0 0 0     0 $header->{'-attachment'} = ($self->{_sendfile} // '') =~ /([^\/]+)$/ ? $1 : '';
468             }
469 0         0 print $r->header($header);
470 0         0 binmode $ofh;
471 0         0 print $self->_slurp($self->{_sendfile});
472             unlink $self->{_sendfile} if $self->{_delete_after_download}
473 0 0       0 }
474             }
475              
476             # exit any eventual temp directory before it is removed by File::Temp
477 0         0 chdir $self->{_curdir};
478              
479 0 0       0 if($self->{_mod_perl}){
480 0         0 my $rh = $r->r;
481             # suppress default Apache response
482 0   0     0 $rh->custom_response($self->{_status_code} || 200, '');
483 0         0 $rh->rflush;
484             }
485              
486 0         0 $self;
487             }
488              
489             sub _slurp {
490 0     0   0 my ($self, $filename) = @_;
491 0 0 0     0 return '' unless $filename && -e -f -r $filename;
492 0         0 open my $fh, '<', $filename;
493 0         0 local $/;
494 0         0 <$fh>;
495             }
496              
497             sub _merge {
498             # merge $_[2] into $_[1]
499             # you must use params directly to make changes
500             # directly on referenced objects, otherwise
501             # perl will work on local copies of them
502              
503 0 0 0 0   0 unless((reftype $_[1] // '') eq 'HASH'){
504 0         0 $_[1] = $_[2];
505 0         0 return;
506             } # if $_[0] points to a scalar or array, $_[1] will prevail
507              
508 0 0       0 unless(scalar keys %{$_[1]}){
  0         0  
509 0         0 $_[1] = $_[2];
510 0         0 return;
511             } # if $_[0] is an empty hash, $_[1] will prevail
512              
513 0         0 my @keys = keys %{$_[1]};
  0         0  
514 0         0 push @keys, keys %{$_[2]};
  0         0  
515 0         0 my $resultOK = 1;
516 0         0 for(@keys){
517 0 0 0     0 if((reftype $_[1]->{$_} // '') ne 'HASH' || (reftype $_[2]->{$_} // '') ne 'HASH'){
      0        
      0        
518 0 0       0 $_[1]->{$_} = defined $_[2]->{$_} ? $_[2]->{$_} : $_[1]->{$_};
519 0         0 next;
520             }
521 0         0 $_[0]->_merge($_[1]->{$_}, $_[2]->{$_});
522             }
523             }
524              
525             =head3 html
526              
527             use this method if you need to return HTML instead of JSON, pass the HTML string as argument
528              
529             yoursubname
530             {
531             ...
532             $j->html($html);
533             }
534              
535             =cut
536              
537             sub html {
538 0     0 1 0 my ($self, $html, $mime) = @_;
539 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
540 0         0 $self->{_mimetype} = $mime;
541 0         0 $self->{_passthrough} = 1;
542 0         0 $self->{_html} = $html;
543 0         0 $self;
544             }
545              
546             =head3 sendfile
547              
548             use this method if you need to return a file instead of JSON, pass the full file path as as argument. MIME type will be set always to I. The last parameter is evaluated as boolean and if true will make JSONP to delete the passed file after it has been downloaded.
549              
550             yoursubname
551             {
552             ...
553             $j->sendfile($fullfilepath, $isTmpFileToDelete);
554             }
555              
556             =cut
557              
558             sub sendfile {
559 0     0 1 0 my ($self, $filepath, $isTmpFileToDelete) = @_;
560 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
561 0         0 $self->{_passthrough} = 1;
562 0         0 $self->{_mimetype} = 'application/octet-stream';
563 0         0 $self->{_sendfile} = $filepath;
564 0         0 $self->{_delete_after_download} = ! ! $isTmpFileToDelete;
565 0         0 $self;
566             }
567              
568             =head3 file
569              
570             call this method to send a file with custom MIME type and/or if you want to set it as inline. The last parameter is evaluated as boolean and if true will make JSONP to delete the passed file after it has been downloaded.
571              
572             $j->file('path to file', $mimetype, $isInline, $isTmpFileToDelete);
573              
574             =cut
575              
576             sub file {
577 0     0 1 0 my ($self, $filepath, $mime, $inline, $isTmpFileToDelete) = @_;
578 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
579 0         0 $self->{_passthrough} = 1;
580 0         0 $self->{_mimetype} = $mime;
581 0         0 $self->{_sendfile} = $filepath;
582 0         0 $self->{_inline} = ! ! $inline;
583 0         0 $self->{_delete_after_download} = ! ! $isTmpFileToDelete;
584 0         0 $self;
585             }
586              
587             =head3 debug
588              
589             call this method before to call C to enable debug mode in a test environment, basically this one will output pretty printed JSON instead of "compressed" one. Furthermore with debug mode turned on the content of session will be returned to the calling page in its own json branch. You can pass a switch to this method (that will be parsed as bool) to set it I or I. It could be useful if you want to pass a variable. If no switch (or undefined one) is passed, the switch will be set as true. Example:
590              
591             $j->debug->run;
592              
593             is the same as:
594              
595             $j->debug(1)->run;
596              
597             =cut
598              
599             sub debug {
600 0     0 1 0 my ($self, $switch) = @_;
601 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH';
602 0 0       0 $switch = defined $switch ? !!$switch : 1;
603 0         0 $self->{_debug} = $switch;
604 0         0 $self->{_pretty} = $switch;
605 0         0 $self;
606             }
607              
608             =head3 pretty
609              
610             call this method before to call C to enable pretty output on I method, basically this one will output pretty printed JSON instead of "compressed" one. You can pass a switch to this method (that will be parsed as bool) to set it I or I. It could be useful if you want to pass a variable. If no switch (or undefined one) is passed, the switch will be set as true. Example:
611              
612             $j->pretty->run;
613              
614             is the same as:
615              
616             $j->pretty(1)->run;
617              
618             =cut
619              
620             sub pretty {
621 0     0 1 0 my ($self, $switch) = @_;
622 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH';
623 0 0       0 $switch = defined $switch ? !!$switch : 1;
624 0         0 $self->{_pretty} = $switch;
625 0         0 $self;
626             }
627              
628             =head3 insecure
629              
630             call this method if you are going to deploy the script under plain http protocol instead of https. This method can be useful during testing of your application. You can pass a switch to this method (that will parsed as bool) to set it on or off. It could be useful if you want to pass a variable. If no switch (or undefined one) is passed, the switch will be set as true.
631              
632             =cut
633              
634             sub insecure {
635 0     0 1 0 my ($self, $switch) = @_;
636 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH';
637 0 0       0 $switch = defined $switch ? !!$switch : 1;
638 0         0 $self->{_insecure_session} = $switch;
639 0         0 $self;
640             }
641              
642             =head3 rest
643              
644             call this method if you want to omit the I parameter and want that a sub with same name of the script will be called instead, so if your script will be I the sub I will be called instead of the one passed with I (that can be omitted at this point). You can pass a switch to this method (that will parsed as bool) to set it on or off. It could be useful if you want to pass a variable. If no switch (or undefined one) is passed, the switch will be set as true.
645              
646             =cut
647              
648             sub rest {
649 0     0 1 0 my ($self, $switch) = @_;
650 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH';
651 0 0       0 $switch = defined $switch ? !!$switch : 1;
652 0         0 $self->{_rest} = $switch;
653 0         0 $self;
654             }
655              
656             =head3 set_session_expiration
657              
658             call this method with desired expiration time for cookie in B, the default behavior is to keep the cookie until the end of session (until the browser is closed).
659              
660             =cut
661              
662             sub set_session_expiration {
663 0     0 1 0 my ($self, $expiration) = @_;
664 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH';
665 0         0 $self->{_session_expiration} = $expiration;
666 0         0 $self;
667             }
668              
669             =head3 query
670              
671             call this method to retrieve a named parameter, $jsonp->query(paramenter_name) will return the value of paramenter_name from query string. The method called without arguments returns all parameters in hash form
672              
673             =cut
674              
675             # TODO remove query method, now it is useless
676             sub query {
677 0     0 1 0 my ($self, $param) = @_;
678 0 0       0 $param ? $self->params->{$param} : $self->params;
679             }
680              
681             =head3 plain_json
682              
683             B parameter will be provided.>
684             call this function to enable output in simple JSON format (not enclosed within jquery_callback_name()... ). Do this only when your script is on the same domain of static content. This method can be useful also during testing of your application. You can pass a switch to this method (that will parsed as bool) to set it on or off. It could be useful if you want to pass a variable. If no switch (or undefined one) is passed, the switch will be set as true.
685              
686             =cut
687              
688             sub plain_json {
689 0     0 1 0 my ($self, $switch) = @_;
690 0 0 0     0 return $self unless (reftype $self // '') eq 'HASH';
691 0 0       0 $switch = defined $switch ? !!$switch : 1;
692 0         0 $self->{_plain_json} = $switch;
693 0         0 $self;
694             }
695              
696             =head3 aaa
697              
698             pass to this method the reference (or the name, either way will work) of the function under which you will manage AAA stuff, like session check, tracking and expiration, and ACL to exposed methods
699              
700             =cut
701              
702             sub aaa {
703 0     0 1 0 my ($self, $sub) = @_;
704 0 0       0 if (ref $sub eq 'CODE') {
705 0         0 $self->{_aaa_sub} = $sub;
706             }
707             else {
708 0         0 my $map = caller() . '::' . $sub;
709             {
710 1     1   9 no strict 'refs'; ## no critic
  1         3  
  1         132  
  0         0  
711 0 0       0 die "given AAA function does not exist" unless defined &$map;
712 0         0 $self->{_aaa_sub} = \&$map;
713             }
714             }
715 0         0 $self;
716             }
717              
718             =head3 login
719              
720             pass to this method the reference (or the name, either way will work) of the function under which you will manage the login process. The function will be called with the current session key (from cookie or automatically created). It will be your own business to save the key-value pair to the storage you choose (database, memcached, NoSQL, and so on). It is advised to keep the initial value associated with the key void, as the serialized I branch of JSONP object will be automatically passed to your aaa function at the end or request cycle, so you should save it from that place. If you want to access/modify the session value do it through the I branch via I<$jsonp-Esession-Ewhatever(value)> or I<$jsonp-E{session}{whatever} = value> or I<$jsonp-E{session}-E{whatever} = value> calls.
721              
722             =cut
723              
724             sub login {
725 0     0 1 0 my ($self, $sub) = @_;
726 0 0       0 if (ref $sub eq 'CODE') {
727 0         0 $self->{_login_sub} = $sub;
728             }
729             else {
730 0         0 my $map = caller() . '::' . $sub;
731             {
732 1     1   8 no strict 'refs'; ## no critic
  1         10  
  1         141  
  0         0  
733 0 0       0 die "given login function does not exist" unless defined &$map;
734 0         0 $self->{_login_sub} = \&$map;
735             }
736             }
737 0         0 $self;
738             }
739              
740             =head3 logout
741              
742             pass to this method the reference (or the name, either way will work) of the function under which you will manage the logout process. The function will be called with the current session key (from cookie or automatically created). It will be your own business to delete the key-value pair from the storage you choose (database, memcached, NoSQL, and so on).
743              
744             =cut
745              
746             sub logout {
747 0     0 1 0 my ($self, $sub) = @_;
748 0 0       0 if (ref $sub eq 'CODE') {
749 0         0 $self->{_logout_sub} = $sub;
750             }
751             else {
752 0         0 my $map = caller() . '::' . $sub;
753             {
754 1     1   8 no strict 'refs'; ## no critic
  1         2  
  1         1815  
  0         0  
755 0 0       0 die "given logout function does not exist" unless defined &$map;
756 0         0 $self->{_logout_sub} = \&$map;
757             }
758             }
759 0         0 $self;
760             }
761              
762             =head3 raiseError
763              
764             call this method in order to return an error message to the calling page. You can add as much messages you want, calling the method several times, it will be returned an array of messages to the calling page. The first argument could be either a string or a B. The second argument is an optional HTTP status code, the default will be 200.
765              
766             =cut
767              
768             sub raiseError {
769 2     2 1 7 my ($self, $message, $code) = @_;
770 2 50 50     11 return $self unless (reftype $self // '') eq 'HASH';
771 2         13 $self->error = \1;
772 2 100 100     5 push @{$self->{errors}}, (reftype $message // '') eq 'ARRAY' ? @$message : $message;
  2         16  
773 2 100       7 $self->{_status_code} = $code if defined $code;
774 2         5 $self;
775             }
776              
777             =head3 graft
778              
779             call this method to append a JSON object as a perl subtree on a node. This is a native method, only function notation is supported, lvalue assignment notation is reserved to autovivification shortcut feature. Examples:
780             $j->subtree->graft('newbranchname', '{"name" : "JSON object", "count" : 2}');
781             print $j->subtree->newbranchname->name; # will print "JSON object"
782             $j->sublist->graft->('newbranchname', '[{"name" : "first one"}, {"name" : "second one"}]');
783             print $j->sublist->newbranchname->[1]->name; will print "second one"
784             my $index = 1; print $j->sublist->newbranchname->$index->name; will print "second one" as well
785              
786             This method will return the reference to the newly added element if added successfully, a false value otherwise.
787              
788             =cut
789              
790             sub graft {
791 0     0 1 0 my ($self, $name, $json) = @_;
792              
793 0 0 0     0 return 0 unless (reftype $self // '') eq 'HASH';
794              
795 0         0 eval{
796 0         0 local $SIG{'__DIE__'};
797 0   0     0 $self->{$name} = JSON->new->decode($json // '');
798             };
799              
800 0 0       0 return 0 if $@;
801              
802             #_bless_tree returns the node passed to it blessed as JSONP
803 0         0 $self->_bless_tree($self->{$name});
804             }
805              
806             =head3 stack
807              
808             call this method to add a JSON object to a node-array. This is a native method, only function notation is supported, lvalue assignment notation is reserved to autovivification shortcut feature. Examples:
809              
810             $j->first->second = [{a => 1}, {b = 2}];
811             $j->first->second->stack('{"c":"3"}');
812             say $j->first->second->[2]->c; # will print 3;
813             my $index = 2; say $j->first->second->$index->c; # will print 3 as well
814              
815             this method of course works only with nodes that are arrays. Be warned that the decoded JSON string will be added as B to the array, so depending of the JSON string you pass, you can have an element that is an hashref (another "node"), a scalar (a "value") or an arrayref (array of arrays, if you want). This method will return the reference to the newly added element if added successfully, a false value otherwise. Combining this to graft method you can do crazy things like this:
816              
817             my $j = JSONP->new;
818             $j->firstnode->graft('secondnode', '{"a" : 1}')->thirdnode = [];
819             $j->firstnode->secondnode->thirdnode->stack('{"b" : 9}')->fourthnode = 10;
820             say $j->firstnode->secondnode->a; # will print 1
821             say $j->firstnode->secondnode->thirdnode->[0]->b; # will print 9
822             say $j->firstnode->secondnode->thirdnode->[0]->fourthnode; # will print 10
823             my $index = 0; say $j->firstnode->secondnode->thirdnode->$index->fourthnode; # will print 10 as well
824              
825             =cut
826              
827             sub stack {
828 0     0 1 0 my ($self, $json) = @_;
829              
830 0 0 0     0 return 0 unless (reftype $self // '') eq 'ARRAY';
831              
832 0         0 eval{
833 0         0 local $SIG{'__DIE__'};
834 0   0     0 push @$self, JSON->new->decode($json // '');
835             };
836 0 0       0 return 0 if $@;
837              
838             #_bless_tree returns the node passed to it blessed as JSONP
839 0         0 $self->_bless_tree($self->[$#{$self}]);
  0         0  
840             }
841              
842             =head3 append
843              
844             call this method to add a Perl object to a node-array. This is a native method, only function notation is supported, lvalue assignment notation is reserved to autovivification shortcut feature. Examples:
845              
846             $j->first->second = [{a => 1}, {b = 2}];
847             $j->first->second->append({c => 3});
848             say $j->first->second->[2]->c; # will print 3;
849              
850             this method of course works only with nodes that are arrays. Be warned that the element will be added as B to the array, so depending of the element you pass, you can have an element that is an hashref (another "node"), a scalar (a "value") or an arrayref (array of arrays, if you want). This method will return the reference to the newly added element if added successfully, a false value otherwise. You can do crazy things like this:
851              
852             my $j = JSONP->new;
853             $j->firstnode->secondnode->a = 1;
854             $j->firstnode->secondnode->thirdnode = [];
855             $j->firstnode->secondnode->thirdnode->append({b => 9})->fourthnode = 10;
856             say $j->firstnode->secondnode->a; # will print 1
857             say $j->firstnode->secondnode->thirdnode->[0]->b; # will print 9
858             say $j->firstnode->secondnode->thirdnode->[0]->fourthnode; # will print 10
859              
860             =cut
861              
862             sub append {
863 0     0 1 0 my ($self, $el) = @_;
864              
865 0 0 0     0 return 0 unless (reftype $self // '') eq 'ARRAY';
866              
867 0         0 push @$self, $el;
868              
869             #_bless_tree returns the node passed to it blessed as JSONP
870 0         0 $self->_bless_tree($self->[$#{$self}]);
  0         0  
871             }
872              
873             =head3 loop
874              
875             when called from an array node it will loop over its elements returning the B> to the current one, so I
876             or copy its value to perform calculation with a copy. Returning the reference assure that loops over arrays items that evaluate as false
877             won't stop until actual array end.
878             Of course this method has the overhead of a function call on every cycle, so use it for convenience on small arrays when performance is not critical.
879             You can also want to use this when the operation to perform on each cycle take a significant amount of time where the overhead becomes negligible.
880             In general avoid to use it in tight high-performance needing loops. Note that the returned item will be a JSONP object (or a JSONP derived type if you subclass it) B, in case the returned item is a raw HASH or ARRAY, it will be blessed with the same class of the array we are looping onto (typically JSONP itself), so the item will hold all the JSONP syntactic sugar and methods.
881             Never exit $array->loop cycles using I to avoid memory leaks, you should avoid to use this method when you expect to early exit the cycle.
882              
883             my $j = JSONP->new;
884             $j->an->array = [
885             [11, 12],
886             [21, 22]
887             ];
888              
889             say $j->an->pretty->serialize;
890              
891             while (my $row = $j->an->array->loop) {
892             while (my $field = $$row->loop){
893             my $acopy = $$field;
894             $$field++;
895             }
896             }
897              
898             say $j->an->pretty->serialize;
899              
900             =cut
901              
902             sub loop {
903 0     0 1 0 my ($self) = @_;
904 0   0     0 my $refself = reftype $self // '';
905 0         0 my $class = ref $self; # bless in cases we have not a deep recursive blessing
906 0 0       0 return undef unless $refself eq 'ARRAY';
907             # use different counter for every array
908 0         0 state $indexes = {};
909 0         0 my $addr = 0 + $self;
910 0         0 my $index = $indexes->{$addr};
911 0         0 $index += 0;
912             # array can change during loop
913 0         0 my $size = @$self;
914              
915 0 0       0 if ($index < $size){
916             # refs are never undef so we can loop
917             # over false scalar items as well
918 0         0 my $item = $self->[$indexes->{$addr}++];
919 0         0 my $reftype = ref $item;
920             # bless the item if it is an unblessed hash or array reference (avoid to touch blessed objects)
921 0 0 0     0 bless $item, $class if $reftype eq 'HASH' || $reftype eq 'ARRAY';
922 0         0 return \$item;
923             } else {
924             # reset counter for next loops
925             # and avoid memory leaks...
926             # note that the loops exited with "last"
927             # will leak few bytes until program end,
928             # with about 8 bytes per loop it's safe
929 0         0 delete $indexes->{$addr};
930 0         0 return undef;
931             }
932             }
933              
934             =head3 serialize
935              
936             call this method to serialize and output a subtree:
937              
938             $j->subtree->graft('newbranchname', '{"name" : "JSON object", "count" : 2}');
939             print $j->subtree->newbranchname->name; # will print "JSON object"
940             $j->sublist->graft->('newbranchname', '[{"name" : "first one"}, {"name" : "second one"}]');
941             print $j->sublist->newbranchname->[1]->name; will print "second one"
942             $j->subtree->newbranchname->graft('subtree', '{"name" : "some string", "count" : 4}');
943             print $j->subtree->newbranchname->subtree->serialize; # will print '{"name" : "some string", "count" : 4}'
944              
945             IMPORTANT NOTE: do not assign any reference to a sub to any node, example:
946              
947             $j->donotthis = sub { ... };
948              
949             for now the module does assume that nodes/leafs will be scalars/hashes/arrays, so same thing is valid for filehandles.
950              
951             =cut
952              
953             sub serialize {
954 0     0 1 0 my ($self) = @_;
955 0         0 my $out;
956 0 0 0     0 my $pretty = (reftype $self // '') eq 'HASH' && $self->{_pretty} ? 1 : 0;
957 0 0       0 eval{
958 0         0 local $SIG{'__DIE__'};
959 0         0 $out = JSON->new->pretty($pretty)->allow_unknown->allow_blessed->convert_blessed->encode($self);
960             } || $@;
961             }
962              
963             =head3 tempdir
964              
965             returns a temporary directory whose content will be removed at the request end.
966             if you pass a relative path, it will be created under the random tmp directory.
967             if creation fails, a boolean false will be retured (void string).
968              
969             my $path = $j->tempdir; # will return something like /tmp/systemd-private-af123/tmp/nRmseALe8H
970             my $path = $j->tempdir('DIRNAME'); # will return something like /tmp/systemd-private-af123/tmp/nRmseALe8H/DIRNAME
971              
972             =cut
973              
974             sub tempdir {
975 0     0 1 0 my ($self, $path) = @_;
976 0 0 0     0 return '' unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
977 0 0       0 return $self->{_tempdir}->dirname unless $path;
978 0         0 return $self->_makePath($path);
979             }
980              
981             =head3 ctwd
982              
983             changes current working directory to a random temporary directory whose content will be removed at the request end.
984             if you pass a path, it will be appended to the temporary directory before cwd'ing on it, bool outcome will be returned.
985             if creation fails, a boolean false will be returned (void string).
986              
987             my $cwdOK = $j->ctwd;
988              
989             =cut
990              
991             sub ctwd {
992 0     0 1 0 my ($self, $path) = @_;
993 0 0 0     0 return '' unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
994 0 0       0 return chdir $self->{_tempdir} unless $path;
995 0         0 $path = $self->_makePath($path);
996 0 0       0 return $path ? chdir $path : '';
997             }
998              
999             sub _makePath {
1000 0     0   0 my ($self, $path) = @_;
1001 0 0 0     0 return '' unless (reftype $self // '') eq 'HASH' && $self->{_is_root_element};
      0        
1002 0         0 my $mkdirerr;
1003 0         0 $path = "$$self{_tempdir}/$path";
1004 0         0 File::Path::make_path($path, {error => \$mkdirerr});
1005 0 0       0 if(@$mkdirerr){
1006 0         0 for my $direrr (@$mkdirerr){
1007 0         0 my ($curdir, $curmessage) = %$direrr;
1008 0         0 say STDERR "error while attempting to create $curdir: $curmessage";
1009             }
1010              
1011             # if creation fails set $path to a "false" string
1012 0         0 $path = '';
1013             }
1014              
1015 0         0 $path;
1016             }
1017              
1018             sub _bless_tree {
1019 183     183   282 my ($self, $node) = @_;
1020 183         305 my $class = ref $self;
1021 183         264 my $refnode = ref $node;
1022             # proceed only with hashes or arrays not already blessed
1023 183 100       347 return $node if $refnode eq $class;
1024             #my $reftype = reftype($node) // '';
1025             #return unless $reftype eq 'HASH' || $reftype eq 'ARRAY';
1026             # to not change class to objects grafted to JSONP tree
1027 90 100 100     284 return $node unless $refnode eq 'HASH' || $refnode eq 'ARRAY';
1028 25         37 bless $node, $class;
1029 25 100       51 if ($refnode eq 'HASH'){
1030 17         63 $self->_bless_tree($node->{$_}) for keys %$node;
1031             }
1032 25 100       49 if ($refnode eq 'ARRAY'){
1033 8         24 $self->_bless_tree($_) for @$node;
1034             }
1035 25         38 $node;
1036             }
1037              
1038             sub TO_JSON {
1039 0     0 0 0 my $self = shift;
1040 0         0 my $output;
1041              
1042 0 0 0     0 return [@$self] if (reftype $self // '') eq 'ARRAY';
1043              
1044 0         0 $output = {};
1045 0         0 for(keys %$self){
1046 0         0 my $skip = 0;
1047              
1048 0 0       0 unless($self->{_debug}){
1049 0 0       0 if($self->{_is_root_element}){
1050 0 0       0 $skip++ if $_ =~ /_sub$/;
1051 0 0       0 $skip++ if $_ eq 'session';
1052 0 0       0 $skip++ if $_ eq 'params';
1053             }
1054 0 0       0 $skip++ if $_ =~ /^_/;
1055             }
1056              
1057 0 0       0 next if $skip;
1058              
1059 0         0 $output->{$_} = $self->{$_};
1060             }
1061 0         0 return $output;
1062             }
1063              
1064             # avoid calling AUTOLOAD on destroy
1065       0     sub DESTROY{}
1066              
1067             sub AUTOLOAD : lvalue {
1068 149     149   894 my $classname = ref $_[0];
1069 149         195 my $validname = q{[^:'[:cntrl:]]{0,1024}};
1070 149         797 our $AUTOLOAD =~ /^${classname}::($validname)$/;
1071 149         320 my $key = $1;
1072 149 50       280 die "illegal key name, must be of $validname form\n$AUTOLOAD" unless $key;
1073 149   50     436 my $arraynode = (reftype($_[0]) // '') eq 'ARRAY';
1074 149 50 33     287 die "array indexes must be unsigned integers" if $arraynode && $key !~ /^\d+$/;
1075 149 100       300 my $miss = want('OBJECT') ? {} : undef;
1076 149 50       7103 my $retval = $arraynode ? $_[0]->[$key] : $_[0]->{$key}; # can be undef
1077 149   100     571 $retval = $_[1] // $retval // $miss;
      100        
1078 149 50 66     284 return '' if want('RVALUE') && ! defined $retval;
1079 149 0 0     6360 return $$retval if want('BOOL') && (reftype($retval) // '') eq 'SCALAR' && $$retval == $$retval % 2;
      33        
      33        
1080              
1081 149 50       7607 if ($arraynode){
1082 0         0 $_[0]->[$key] = $retval;
1083 0         0 $_[0]->_bless_tree($_[0]->[$key]);
1084 0         0 return $_[0]->[$key];
1085             } else {
1086 149         247 $_[0]->{$key} = $retval;
1087 149         356 $_[0]->_bless_tree($_[0]->{$key});
1088 149         862 return $_[0]->{$key};
1089             }
1090             }
1091              
1092             =head1 NOTES
1093              
1094             =head2 NOTATION CONVENIENCE FEATURES
1095              
1096             In order to achieve autovivification notation shortcut, this module does not make use of perlfilter but does rather some gimmick with AUTOLOAD. Because of this, when you are using the convenience shortcut notation you cannot use all the names of public methods of this module (such I, I, I, and others previously listed on this document) as hash keys, and you must always use hash keys composed from any Unicode char that is not a posix defined control char, ' (apostrophe) and : (colon). You can also use keys composed of only digits, but then it must not be a literal, put it in a variable. In that case the key wil be interpreted as array index or hash key depending of the type of node you are calling it upon. The total lenght of the key must be not bigger than 1024 Unicode chars, this is an artificial limit set for security purposes. You can still set/access hash branches of whatever name using the brace notation. It is nonetheless highly discouraged the usage of underscore beginning keys through brace notation, at least at the top level of response hash hierarchy, in order to avoid possible clashes with private variable members of this very module.
1097              
1098             =head2 MINIMAL REQUIREMENTS
1099              
1100             this module requires at least perl 5.10 for its usage of "defined or" // operator
1101              
1102             =head2 DEPENDENCIES
1103              
1104             JSON and Want are the only non-core module used by this one, use of JSON::XS is strongly advised for the sake of performance. JSON::XS is been loaded transparently by JSON module when installed. CGI module is a core one at the moment of writing, but deprecated and likely to be removed from core modules in next versions of Perl.
1105              
1106             =head1 SECURITY
1107              
1108             Remember to always:
1109              
1110             =over 4
1111              
1112             =item 1. use taint mode
1113              
1114             =item 2. use parametrized queries to access databases via DBI
1115              
1116             =item 3. avoid as much as possible I, I, I, and so on
1117              
1118             =item 4. use SSL when you are keeping track of sessions
1119              
1120             =back
1121              
1122             =head1 HELP and development
1123              
1124             the author would be happy to receive suggestions and bug notification. If somebody would like to send code and automated tests for this module, I will be happy to integrate it.
1125             The code for this module is tracked on this L.
1126              
1127             =head1 LICENSE
1128              
1129             This library is free software and is distributed under same terms as Perl itself.
1130              
1131             =head1 COPYRIGHT
1132              
1133             Copyright 2014-2038 by Anselmo Canfora.
1134              
1135             =cut
1136              
1137             1;