+3.13 - May 20, 2002
+- Added documentation:
+ - PLP
+ - PLP::FAQ
+ - PLP::Fields
+ - PLP::Functions
+- Added predeclared of hashes for use-strict-users ("our"/"use vars"
+ is no longer necessary for the PLP hashes)
+- Added plp.vim for vim syntax highlighting to the distribution
+- Added error reportig to Counter, ReadFile and WriteFile
+- Changed ReadFile and WriteFile to use lexical filehandles
+- Changed PLP::Functions to use Fcntl for improved portability
+- Cleaned up PLP::Fields: removed the PLPdummies
+- Fixed DELETE, EXISTS and FIRSTKEY in PLP::Tie::Delay (added PLPdummy there)
+
3.12 - May 18, 2002
- Fixed strict-violation in PLP.pm that happened only without mod_perl
PLP.pm
README
plp.cgi
+plp.vim
test.pl
+PLP/FAQ.pod
PLP/Fields.pm
PLP/Functions.pm
PLP/Tie/Delay.pm
-package PLP;
+#--------------#
+ package PLP;
+#--------------#
use v5.6;
use strict;
-our $VERSION = '3.12';
+our $VERSION = '3.13';
# subs in this package:
# sendheaders Send headers
PLP::Fields::doit();
{
package PLP::Script;
+ use vars qw(%headers %header %cookies %cookie %get %post %fields);
*headers = \%header;
*cookies = \%cookie;
PLP::Functions->import();
model: one can just use the normal Perl constructs. PLP runs under mod_perl
for speeds comparable to those of PHP, but can also be run as a CGI script.
+=head2 PLP Syntax
+
+=over 22
+
+=item C<< <: perl_code(); :> >>
+
+With C<< <: >> and C<< :> >>, you can add Perl code to your document. This is
+what PLP is all about. All code outside of these tags is printed. It is
+possible to mix perl language constructs with normal HTML parts of the document:
+
+ <: unless ($ENV{REMOTE_USER}) { :>
+ You are not logged in.
+ <: } :>
+
+C<< :> >> always stops a code block, even when it is found in a string literal.
+
+=item C<< <:= $expression :> >>
+
+Includes a dynamic expression in your document. The expression is evaluated in
+list context. Please note that the expression should not end a statement: avoid
+semi-colons. No whitespace may be between C<< <: >> and the equal sign.
+
+C<< foo <:= $bar :> $baz >> is like C<< <: print 'foo ', $bar, ' baz'; :> >>.
+
+=item C<< <(filename)> >>
+
+Includes another file before the PLP code is executed. The file is included
+literally, so it shares lexical variables. Because this is a compile-time tag,
+it's fast, but you can't use a variable as the filename. You can create
+recursive includes, so beware of that! Whitespace in the filename is not
+ignored so C<< <( foo.txt)> >> includes the file named C< foo.txt>, including
+the space in its name. A compile-time alternative is include(), which is
+described in L<PLP::Functions>.
+
+=back
+
+=head2 PLP Functions
+
+These are described in L<PLP::Functions>.
+
+=head2 PLP Variables
+
+=over 22
+
+=item $ENV{PLP_NAME}
+
+The URI of the PLP document, without the query string. (Example: C</foo.plp>)
+
+=item $ENV{PLP_FILENAME}
+
+The filename of the PLP document. (Example: C</var/www/index.plp>)
+
+=item $PLP::VERSION
+
+The version of PLP.
+
+=item $PLP::DEBUG
+
+Controls debugging output, and should be treated as a bitmask. The least
+significant bit (1) controls if run-time error messages are reported to the
+browser, the second bit (2) controls if headers are sent twice, so they get
+displayed in the browser. A value of 3 means both features are enabled. The
+default value is 1.
+
+=item $PLP::ERROR
+
+Contains a reference to the code that is used to report run-time errors. You
+can override this to have it in your own design, and you could even make it
+report errors by e-mail. The sub reference gets two arguments: the error message
+as plain text and the error message with special characters encoded with HTML
+entities.
+
+=item %header, %cookie, %get, %post, %fields
+
+These are described in L<PLP::Fields>.
+
+=back
+
+=head2 Things that you should know about
+
+Not only syntax is important, you should also be aware of some other important
+features. Your script runs inside the package C<PLP::Script> and shouldn't
+leave it. This is because when your script ends, all global variables in the
+C<PLP::Script> package are destroyed, which is very important if you run under
+mod_perl (they would retain their values if they weren't explicitly destroyed).
+
+Until your first output, you are printing to a tied filehandle C<PLPOUT>. On
+first output, headers are sent to the browser and C<STDOUT> is selected for
+efficiency. To set headers, you must assign to C<$header{ $header_name}> before
+any output. This means the opening C<< <: >> have to be the first characters in
+your document, without any whitespace in front of them. If you start output and
+try to set headers later, an error message will appear telling you on which
+line your output started.
+
+Because the interpreter that mod_perl uses never ends, C<END { }> blocks won't
+work properly. You should use C<PLP_END { };> instead. Note that this is a not
+a built-in construct, so it needs proper termination with a semi-colon (as do
+<eval> and <do>).
+
+Under mod_perl, modules are loaded only once. A good modular design can improve
+performance because of this, but you will have to B<reload> the modules
+yourself when there are newer versions.
+
+The special hashes are tied hashes and do not always behave the way you expect,
+especially when mixed with modules that expect normal CGI environments, like
+CGI.pm. Read L<PLP::Fields> for information more about this.
+
=head1 WEBSITE
For now, all documentation is on the website. Everything will be POD one day,
=head1 FAQ
A lot of questions are asked often, so before asking yours, please read the
-FAQ that is located at http://plp.juerd.nl/faq.plp
+FAQ at L<PLP::FAQ>.
=head1 NO WARRANTY
--- /dev/null
+=head1 Frequently Asked Questions about PLP
+
+=over 10
+
+=item What does PLP stand for?
+
+PerlPage. The name used to be HTMPL, but HyperText Markup with Perl Language was too long.
+
+=item Is PLP hard to install?
+
+No, it actually is very simple and easy. Quick startup hints are in the PLP main
+documentation, extensive installation instructions are on the PLP website.
+
+=item Is Perl code harder than PHP code?
+
+Yes, it is. But when you get used to Perl, you will probably dislike PHP for
+the rest of your life. Perl is faster and far more powerful. For both Perl
+beginners and more advanced Perl coders, PerlMonks is a good Perl forum community.
+(Please note: PLP is not Perl. Perl is a complete programming language and is
+not restricted to web based applications. PLP B<uses> Perl, but many people
+use Perl without PLP.
+
+=item Can PLP be used with mod_perl?
+
+Yes. As of 3.00, PLP can be used with mod_perl! And it's very fast!
+
+=item You seem to promote dirty programming. Can I use strict with PLP?
+
+PLP can be used for quick-and-dirty hacks in a way similar to PHP. However, it
+is suitable for larger applications as well. You can use strict if you want.
+mod_perl Users might like to know that globals are automatically destroyed (as
+long as you do not switch packages).
+
+=item How can I make PLP faster?
+
+With mod_perl, PLP is a lot faster than with CGI. CGI scripts execute an
+external interpreter, but mod_perl is a Perl interpreter inside Apache.
+
+=item I already use mod_perl, can I make my scripts even faster?
+
+Well, you already have scripts that probably are faster than PHP equivalents,
+but speed maniacs always want more. Modules are cached, so with a proper module
+design, you can add a little more speed.
+
+=item Can I use Perl's CGI module with PLP?
+
+You certainly can! If you do not want %get and %post and the like, just not use
+them. They will be generated on first access, so if you never access them, the
+hashes are never filled.
+
+If you want to use CGI.pm's header functions, C<select STDOUT;> first, to break
+out of PLP's tied C<PLPOUT> filehandle.
+
+=item Why does C<< <($filename)> >> not work?
+
+C<< <(...)> >> is a compile-time tag, opposed to C<include()>, which is evaluated at
+run-time. At compile time, variables are not yet known, and PLP will try to
+include a file literally called C<$filename>.
+
+ <: $filename = 'foo.inc.plp'; include($filename); :>
+
+=item Why do my variables not work in my C<include()>d file?
+
+That is because your variable is lexical (declared with C<my>), and the file is
+evaluated in its own scope, just as with Perl's built-in C<do> and C<require>.
+You can pass variables through subroutine parameters or by using globals
+variables. Another solution is using PLP's C<< <(...)> >> tag.
+
+=item But why do they work with C<< <()> >> then?
+
+Because it places the external file is placed inside of the other,
+B<before> the code is executed (at compile-time).
+
+=item Why do my C<END> blocks never get executed?
+
+If they are not, you are probably running under mod_perl. The blocks are
+executed when the interpreter stops, but the mod_perl interpreter is not exited
+after the PLP script has ended. Use C<PLP_END> blocks instead. Please note that
+C<PLP_END> is a normal statement, so you may need a semicolon.
+
+ <html><body>
+ <: PLP_END { :>
+ </body></html>
+ <: } :>
+
+=item Can I disable the error messages?
+
+You can not disable compile-time errors (syntax errors), but you can disable
+run-time errors. To do so, set the 0-bit (1) of C<$PLP::DEBUG> off. If you only
+want error reporting disabled for a single command, use Perl's C<eval BLOCK>
+function (not C<eval "">, but C<eval {}>, which is not slow or insecure.).
+
+ <: $PLP::DEBUG ^= 1 if $PLP::DEBUG & 1; :>
+
+=item Can I have my own error messages?
+
+Yes, you can! Of course, you can not override compile-time errors like syntax
+errors, but run-time error messages use C<$PLP::ERROR>, which is a reference to a
+sub that gets two arguments: the error message itself, and an html-encoded
+version.
+
+ <:
+ $PLP::ERROR = sub {
+ my ($plain, $html) = @_;
+ print '<font color="red">', $html, '</font>';
+ };
+ :>
+
+=item Is there a way to see the headers that PLP sends?
+
+There is. Set C<$PLP::DEBUG>'s 1-bit (2), and it will output a plain text header
+before outputting the other one.
+
+ <: $PLP::DEBUG ^= 2 unless $PLP::DEBUG & 2 :>
+
+=back
+
+=cut
+
#----------------------#
use strict;
-=head1 PLP::Fields
-
-Has only one function: doit(), which ties the hashes %get, %post, %fields and %header in
-PLP::Script. Also generates %cookie immediately.
-
- PLP::Fields::doit();
-
-This module is part of the PLP internals. Don't use it yourself.
-
-=cut
-
+# Has only one function: doit(), which ties the hashes %get, %post, %fields and %header in
+# PLP::Script. Also generates %cookie immediately.
sub doit {
tie %PLP::Script::get, 'PLP::Tie::Delay', 'PLP::Script::get', sub {
my %get;
- my $get;
- $get = $ENV{QUERY_STRING};
+ my $get = $ENV{QUERY_STRING};
if ($get ne ''){
for (split /[&;]/, $get) {
my @keyval = split /=/, $_, 2;
} else {
read(*STDIN, $post, $ENV{CONTENT_LENGTH});
}
- if (defined($post) && $post ne '' &&
- ($ENV{CONTENT_TYPE} eq '' || $ENV{CONTENT_TYPE} eq 'application/x-www-form-urlencoded')){
+ if (defined $post
+ and $post ne ''
+ and $ENV{CONTENT_TYPE} =~ m!^(?:application/x-www-form-urlencoded|$)!
+ ){
for (split /&/, $post) {
my @keyval = split /=/, $_, 2;
PLP::Functions::DecodeURI(@keyval);
};
tie %PLP::Script::fields, 'PLP::Tie::Delay', 'PLP::Script::fields', sub {
- $PLP::Script::get{PLPdummy}, $PLP::Script::post{PLPdummy}; # Trigger creation
- return {%PLP::Script::get, %PLP::Script::post}
+# $PLP::Script::get{PLPdummy}, $PLP::Script::post{PLPdummy}; # Trigger creation
+# No longer necessary, as PLP::Tie::Delay has been fixed since 3.00
+# And fixed even more in 3.13
+ return { %PLP::Script::get, %PLP::Script::post };
};
tie %PLP::Script::header, 'PLP::Tie::Headers';
}
1;
+
+=head1 NAME
+
+PLP::Fields - Special hashes for PLP
+
+=head1 DESCRIPTION
+
+For your convenience, PLP uses hashes to put things in. Some of these are tied
+hashes, so they contain a bit magic. For example, building the hash can be
+delayed until you actually use the hash.
+
+=over 10
+
+=item C<%get> and C<%post>
+
+These are built from the C<key=value&key=value> (or C<key=value;key=value>
+strings in query string and post content. C<%post> is not built if the content
+type is not C<application/x-www-form-urlencoded>. In post content, the
+semi-colon is not a valid separator.
+
+These hashes aren't built until they are used, to speed up your script if you
+don't use them. Because POST content can only be read once, you can C<use CGI;>
+and just never access C<%post> to avoid its building.
+
+With a query string of C<key=firstvalue&key=secondvalue>, C<$get{key}> will
+contain only C<secondvalue>. You can access both elements by using the array
+reference C<$get{'@key'}>, which will contain C<[ 'firstvalue', 'secondvalue'
+]>.
+
+=item C<%fields>
+
+This hash combines %get and %post, and triggers creation of both. POST gets
+precedence over GET (note: not even the C<@>-keys contain both values).
+
+=item C<%cookie>, C<%cookies>
+
+This is built immediately, because cookies are usually short in length. Cookies
+are not automatically url-decoded.
+
+=item C<%header>, C<%headers>
+
+In this hash, you can set headers. Underscores are converted to normal minus
+signs, so you can leave out quotes. The hash is case insensitive: the case used
+when sending the headers is the one you used first. The following are equal:
+
+ $header{CONTENT_TYPE}
+ $header{'Content-Type'}
+ $header{Content_Type}
+ $headers{CONTENT_type}
+
+=back
+
+=head1 AUTHOR
+
+Juerd Waalboer <juerd@juerd.nl>
+
+=cut
+
package PLP::Functions;
#-------------------------#
use base 'Exporter';
+use Fcntl qw(:flock);
use strict;
our @EXPORT = qw/HiddenFields Entity DecodeURI EncodeURI Entity include PLP_END
}
sub ReadFile ($) {
- local *READFILE;
local $/ = undef;
- open (READFILE, '<', $_[0]);
- my $r = <READFILE>;
- close READFILE;
+ open (my $fh, '<', $_[0]) or do {
+ PLP::error("Cannot open $_[0] for reading ($!)", 1);
+ return undef;
+ };
+ my $r = readline $fh;
+ close $fh;
return $r;
}
sub WriteFile ($$) {
- local *WRITEFILE;
- open (WRITEFILE, '>', $_[0]);
- flock WRITEFILE, 2;
- print WRITEFILE $_[1];
- close WRITEFILE;
+ open (my $fh, '>', $_[0]) or do {
+ PLP::error("Cannot open $_[0] for writing ($!)", 1);
+ return undef;
+ };
+ flock $fh, LOCK_EX;
+ print $fh $_[1] or do {
+ PLP::error("Cannot write to $_[0] ($!)");
+ return undef;
+ };
+ close $fh or do {
+ PLP::error("Cannot close $_[0] ($!)");
+ return undef;
+ };
+ return 1;
}
sub Counter ($) {
- local *COUNTER;
local $/ = undef;
- open COUNTER, '+<', $_[0] or
- open COUNTER, '>', $_[0] or return undef;
- flock COUNTER, 2;
- seek COUNTER, 0, 0;
- my $counter = <COUNTER>;
- seek COUNTER, 0, 0;
- truncate COUNTER, 0;
- print COUNTER ++$counter;
- close COUNTER;
+ my $fh;
+ open $fh, '+<', $_[0] or
+ open $fh, '>', $_[0] or return undef;
+ flock $fh, 2;
+ seek $fh, 0, 0;
+ my $counter = <$fh>;
+ seek $fh, 0, 0;
+ truncate $fh, 0;
+ print $fh ++$counter or return undef;
+ close $fh or return undef;
return $counter;
}
return defined wantarray ? $$ref : undef;
}
-
1;
+
+=head1 NAME
+
+PLP::Functions - Functions that are available in PLP documents
+
+=head1 DESCRIPTION
+
+The functions are exported into the PLP::Script package that is used by PLP documents. Although uppercased letters are unusual in Perl, they were chosen to stand out.
+
+Most of these functions are context-hybird. Before using them, one should know about contexts in Perl. The three major contexts are: B<void>, B<scalar> and B<list> context. You'll find more about context in L<perlfunc>.
+
+Some context examples:
+
+ print foo(); # foo is in list context (print LIST)
+ foo(); # foo is in void context
+ $bar = foo(); # foo is in scalar context
+ @bar = foo(); # foo is in list context
+ length foo(); # foo is in scalar context (length EXPR)
+
+=head2 The functions
+
+=over 10
+
+=item Include FILENAME
+
+Executes another PLP file, that will be parsed (i.e. code must be in C<< <: :> >>). As with Perl's C<do>, the file is evaluated in its own lexical file scope, so lexical variables (C<my> variables) are not shared. PLP's C<< <(filename)> >> includes at compile-time, is faster and is doesn't create a lexical scope (it shares lexical variables).
+
+=item include FILENAME
+
+An alias for C<Include>.
+
+=item PLP_END BLOCK
+
+Adds a piece of code that is executed when at the end of the PLP document. This is useful when creating a template file:
+
+ <html><body> <!-- this is template.plp -->
+ <: PLP_END { :>
+ </body></html>
+ <: } :>
+
+ <(template.plp)> <!-- this is index.plp -->
+ Hello, world!
+
+You should use this function instead of Perl's built-in C<END> blocks, because those do not work properly with mod_perl.
+
+=item Entity LIST
+
+Replaces HTML syntax characters by HTML entities, so they can be displayed literally. You should always use this on user input (or database output), to avoid cross-site-scripting vurnerabilities. This function does not do everything the L<HTML::Entity> does.
+
+In void context, B<changes> the values of the given variables. In other contexts, returns the changed versions.
+
+ <: print Entity($user_input); :>
+
+=item EncodeURI LIST
+
+Replaces characters by their %-encoded values.
+
+In void context, B<changes> the values of the given variables. In other contexts, returns the changed versions.
+
+ <a href="/foo.plp?name=<:= EncodeURI($name) :>">Link</a>
+
+=item DecodeURI LIST
+
+Decodes %-encoded strings.
+
+In void context, B<changes> the values of the given variables. In other contexts, returns the changed versions.
+
+=item ReadFile FILENAME
+
+Returns the contents of FILENAME in one large string. Returns undef on failure.
+
+=item WriteFile FILENAME, STRING
+
+Writes STRING to FILENAME (overwrites FILENAME if it already exists). Returns true on success, false on failure.
+
+=item Counter FILENAME
+
+Increases the contents of FILENAME by one and returns the new value. Returns undef on failure. Fails silently.
+
+ You are visitor number <:= Counter('counter.txt') :>.
+
+=item AutoURL STRING
+
+Replaces URLs (actually, replace things that look like URLs) by links.
+
+In void context, B<changes> the value of the given variable. In other contexts, returns the changed version.
+
+ <: print AutoURL(Entity($user_input)); :>
+
+=item AddCookie STRING
+
+Adds a Set-Cookie header. STRING must be a valid Set-Cookie header value.
+
+=back
+
+=head1 AUTHOR
+
+Juerd Waalboer <juerd@juerd.nl>
+
+=cut
+
sub _replace {
my ($self) = @_;
- untie %{$self->[0]};
- %{$self->[0]} = %{ $self->[1]->() };
+ untie %{ $self->[0] };
+ %{ $self->[0] } = %{ $self->[1]->() };
}
sub TIEHASH {
my ($class, $hash, $source) = @_;
- return bless [$hash, $source], $class;
+ return bless [ $hash, $source ], $class;
}
sub FETCH {
my ($self, $key) = @_;
$self->_replace;
- return ${$self->[0]}{$key};
+ return ${ $self->[0] }{$key};
}
sub STORE {
my ($self, $key, $value) = @_;
$self->_replace;
- return ${$self->[0]}{$key} = $value;
+ return ${ $self->[0] }{$key} = $value;
}
sub DELETE {
my ($self, $key) = @_;
$self->_replace;
- return delete ${$self->[0]}{key};
+ return delete ${ $self->[0] }{$key};
}
sub CLEAR {
my ($self) = @_;
$self->_replace;
- return %{$self->[0]};
+ return %{ $self->[0] };
}
sub EXISTS {
my ($self, $key) = @_;
$self->_replace;
- return exists ${$self->[0]}{key};
+ return exists ${ $self->[0] }{$key};
}
sub FIRSTKEY {
my ($self) = @_;
$self->_replace;
- return exists ${$self->[0]}{key};
+ return 'PLPdummy';
}
sub NEXTKEY {
my ($self) = @_;
- $self->_replace;
- return each %$$self;
+ # Let's hope this never happens. (It's shouldn't.)
+ return undef;
}
sub UNTIE { }
--- /dev/null
+" Vim syntax file
+" Language: PLP (Perl in HTML)
+" Maintainer: Juerd <juerd@juerd.nl>
+" Last Change: 2002 May 19
+" Cloned From: aspperl.vim
+
+" Add to filetype.vim the following line (without quote sign):
+" au BufNewFile,BufRead *.plp setf plp
+
+" For version 5.x: Clear all syntax items
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+ syntax clear
+elseif exists("b:current_syntax")
+ finish
+endif
+
+if !exists("main_syntax")
+ let main_syntax = 'perlscript'
+endif
+
+if version < 600
+ so <sfile>:p:h/html.vim
+ syn include @PLPPerlScript <sfile>:p:h/perl.vim
+else
+ runtime! syntax/html.vim
+ unlet b:current_syntax
+ syn include @PLPPerlScript syntax/perl.vim
+endif
+
+syn cluster htmlPreproc add=PLPPerlScriptInsideHtmlTags
+
+syn region PLPPerlScriptInsideHtmlTags keepend matchgroup=Delimiter start=+<:=\=+ end=+:>+ contains=@PLPPerlScript
+
+syn cluster htmlPreproc add=PLPIncludeTag
+
+syn region PLPIncludeTag keepend matchgroup=Delimiter start=+<(+ end=+)>+ contains=@PLPIncludeFilename
+
+let b:current_syntax = "plp"