Title: More PHP
1More PHP
2What we'll cover
- A short history of php
- Parsing
- Variables
- Arrays
- Operators
- Control Structures
- Forms
- Functions
- Accessing the shell
- Sockets
- Regular Expressions
3Forms
- Obviously, one of the things you need to handle
are forms - You can make an html only form, and submit that
to a php file - You can also make a single php file that makes
the form, and submit that to itself, and on the
submit, present a different page, and then after
a confirmation present a third page, and so on,
and so on. - We'll talk about both
4Simple Form
ltform action"./simpleform.php" method"GET"
name"choices"gt ltpgtWhat is the meaning of
life?lt/pgt ltpgtlttextarea rows"1" cols"50"
wrap"virtual" name "answer"gtlt/textareagt
lt/pgt ltpgtWhat is your name?lt/pgt ltpgtlttextarea
rows"1" cols"50" wrap"virtual" name
"name"gtlt/textareagtlt/pgt ltpgtWhere is your
car?lt/pgt ltpgtlttextarea rows"1" cols"50"
wrap"virtual" name "car"gtlt/textareagtlt/p
gt ltpgtltinput type"submit" value"Submit"
align"middle" /gtlt/pgt lt/formgt
simpleform/simpleform.html
5Simple Form php
lt?php if (count(_GET) lt 1) echo "no
GETs!" else echo "Here is the GET
array" echo "ltpregt" print_r(_GET)
echo "lt/pregt" ?gt
- The php that catches the form reads the GET array
- All this one does is used print_r() to show the
results - Can you think of any advantages or disadvantages
to using two separate pages this way? - What about the first condition? When is it met?
simpleform/simpleform.php
6Simple form to itself
- To do all of this with one php file, we need a
way to detect where we are in the process - So on first load, we present the form
- On second load, after submitting the form, we
process results
7simpleform_self.php
lt?php if (!_GET) echo "ltform
action\"" . _SERVER"PHP_SELF" . "\"
method\"GET\" name\"choices\"gt\n"
echo "ltpgtWhat is the meaning of life?lt/pgt
ltpgtlttextarea rows\"1\" cols\"50\"
wrap\"virtual\" name
\"answer\"gtlt/textareagtlt/pgt\n" echo "ltpgtWhat
is your name?lt/pgt ltpgtlttextarea rows\"1\"
cols\"50\" wrap\"virtual\" name
\"name\"gtlt/textareagtlt/pgt\n" echo "ltpgtWhere
is your car?lt/pgt ltpgtlttextarea rows\"1\"
cols\"50\" wrap\"virtual\" name
\"car\"gtlt/textareagtlt/pgt\n" echo 'ltinput
type"hidden" value "111" name "key_number"
/gt' echo 'ltpgtltinput type"submit"
value"Submit" align"middle" /gtlt/pgt'
8simpleform_self.php
else echo "Here is the GET array"
echo "ltpregt" print_r(_GET) echo
"lt/pregt" ?gt lt/formgt lt/bodygt lt/htmlgt
- When the form is submitted, the url has a GET
array - So it passes past the if statement, and lands on
the else, where we process data - Again, we're just print_r()'ing the results
- There's an error here, can you spot it?
9Sessions
- A very useful aspect to php is the ability to
maintain session data - To do this, you create a session, then load it
with data - The session data is stored server side, usually
in /etc, and is keyed (but not encrypted), and
remains for that url and remote ip
10simpleform_self_session.php
- Sessions need to be created before any html
headers are sent, so do this at the very top of
the page - Use session_register() to create slots for
variables - After these variables are populated, you can
access them via the _SESSION array - In this example we'll define a key (randomly
generated) and a counter (to track steps)
11simpleform_self_session.php
lt?php // Start a session, and set a key and a
counter // A session is a method of tracking
state for a given // browser session, and allows
storage of data on the server session_start() //
create a session session_register('session_key')
// register a session var for a key to
// track this session
// this is done to
help detect reloads session_register('session_coun
ter') // session counter is used to
//
track where we are in
// a session ?gt
12simpleform_self_session.php
- Now, we can check see if the key is set, if not,
we're on a first load and we generate a key and
set the counter to 0.
// if the session_key is not yet set, generate a
random number for the key // and set the
session_counter to 0 (this situation indicates
that the form // is being loaded for the first
time) if (!_SESSION'session_key')
_SESSION'session_key' rand()
_SESSION'counter' 0
13simpleform_self_session.php
- Then we build the form--note the use of PHP_SELF,
and the hidden input.
// If we don't have key_number, then the user
hasn't filled in the form if (!_GET"key_number"
) echo "ltform action\"" .
_SERVER"PHP_SELF" . "\" method\"GET\"
name\"choices\"gt\n" echo "ltpgtWhat is the
meaning of life?lt/pgtltpgtlttextarea rows\"1\"
cols\"50\" wrap\"virtual\" name
\"answer\"gtlt/textareagtlt/pgt\n"
echo 'ltinput type"hidden" value "' .
_SESSION'session_key' . '" name
"key_number" /gt' echo 'lt/formgt'
_SESSION'counter' 1
14simpleform_self_session.php
- Next, we have an elseif that looks to see that we
have the data we need and that there's a key and
that counter is set to one - Then we start by printing the results.
elseif (_SESSION'session_key'
_GET"key_number" _SESSION'counter' 1
(_GET"answer" _GET"name"
_GET"car")) echo "Here is the GET
array" echo "ltpregt" print_r(_GET)
echo "lt/pregt"
15simpleform_self_session.php
- and then we make a confirmation form and set
the counter to 2 - And again we submit the form to itself
echo "ltform action\"" . _SERVER"PHP_SELF" .
"\" method\"GET\" name\"choices\"gt\n" echo
'ltinput type"hidden" value "' .
_SESSION'session_key' . '" name
"key_number" /gt' echo 'ltpgtAre these ok?lt/pgt'
echo 'ltinput type"radio" value"yes" name
"yes_no" /gtyes ltinput type"radio"
value"no" name "yes_no" /gtno' echo 'ltpgtltinput
type"submit" value"Submit" align"middle"
/gtlt/pgt' echo 'lt/formgt'
_SESSION'counter' 2
16simpleform_self_session.php
- Next, we check for the confirmation and kill the
session, if things are good here, we do good
things..
// Check again that the session_key matches the
key_number, and that we're on step number three,
and for "yes" elseif (_SESSION'session_key'
_GET"key_number" _SESSION'counter' 2
_GET"yes_no" "yes") echo
'ltpgtCool, everything\'s copacetic.lt/pgt' //
Here you'd do the Good Thing, whatever that is.
// Destroy the session session_unset()
session_destroy()
17simpleform_self_session.php
- And we check for badness.
// Check again that the session_key matches the
key_number, and that we're on step number three,
and for "no" elseif (_SESSION'session_key'
_GET"key_number" _SESSION'counter' 2
_GET"yes_no" "no") echo
'ltpgtOops, sorry, lta href"./simpleform_self_sessio
n.php"gt try again?lt/agtlt/pgt' // Here
you'd do the Bad Thing, whatever that is
// Destroy the session session_unset()
session_destroy()
18simpleform_self_session.php
- And a final sanity check
- This is not particularly clean, since we're not
checking much, and esp. because this is where we
land if not all fields are filled in--better ways
to deal with this?
// If no conditions matched, assume something
went wrong and try to cope else echo
'ltpgtI\'m sorry, I found a problem, perhaps you
didn\'t fill in all of the boxes. lta
href"./simpleform_self_session.php"gt
Continue?lt/agtlt/pgt' // Destroy the session
session_unset() session_destroy()
19User Defined Functions
- Like javascript, php supports user defined
functions - Declare your functions towards the top of your
php file--this is not a requirement, just good
practice - Functions are created when the program is read,
and before execution - Or, build an exterior file with commonly used
functions, and give that a version number. Then
require that file to reuse your code (see the
general functions file in the samples for
examples I use)
20Passing Data
- The simplest way to pass data into a function is
through that functions argument list of variable
(and arrays are a type of variable)// wrap the
variable in pfunction echo_p(wrapped_item)
echo "ltpgtwrapped_itemlt/pgt" - You can also set default values, but watch
placement--defaults to the right please// wrap
the variable in h// default to level 2function
echo_h(wrapped_item, level"2") echo "lth"
. level . "gtwrapped_itemlt/h" . level. "gt\n"
12
21Scope
- Functions themselves have global scope--functions
defined within functions are available everywhere - Variables assigned or modified inside of a
function have their value only within the
function - A variable can be declared global within a
function, however, if you want to pass that
variable into and out of the function - Generally, it's better to leave variable scope
limited, this makes the function portable.
15
22Functions have Global Scope
- Unlike variables, functions are global in scope
- But a function doesn't execute until called
- Here, foo() is used to create bar()
- Once foo() is called, bar() exists
- bar() is available outside of foo()
function foo() function bar() echo
"I don't exist until foo() is called.\n"
/ We can't call bar() yes since it
doesn't exist. / foo() / Now we can call
bar(), foo()'s processing has made it
accessible. / bar() ?gt
this example is 17.3 from the php manual, up as
14_functions.php See also 15_function_scope.php
23Functions and Default Values
function good_echo_h(wrapped_item, level"2")
echo "lth" . level . "gtwrapped_itemlt/h"
. level. "gt\n" function bad_echo_h(level"
2", wrapped_item) echo "lth" . level .
"gtwrapped_itemlt/h" . level. "gt\n"
good_echo_h("A good heading") good_echo_h("A
good heading at level 1", 1) bad_echo_h(2, "A
bad heading with level set") bad_echo_h("A bad
heading with no level set")
- Defaults must occur to the right
12_function_defaults.php
24An Example Building an FAQ
- Core program calls 5 functions, 3 local and 2
external - 1 Data file and some variables
- This kind of program structure makes
understanding a large program much easier
25aform
- This is set up in samples and the labs section,
both, so you can do the lab to figure this out - Based on a general survey form I came up with a
few years ago
26aform
- Uses the general_functions file
- Data is external in a text file
- The code that makes the various types of question
in the form is also external - By keeping data and question types separate, it's
easy to use the same program to make different
surveys
27Leveraging the Shell
28Back tick and shell_exec()
- Php uses backticks (olde school) and the function
shell_exec() to process shell commands (usually
through bash) - Shell commands execute with the environment and
rights of the web server - Opening a shell is not lightweight in terms of
ram, so keep in mind it can be expensive - Very useful if a wheel already exists in your
setup (Netcheck is an example).
04
2910_show_source.php
- I wanted to be able to display source code for
the php programs - There is a function, highlight_file(), that will
handle syntax highlighting for a php file - I could have written the whole thing in php (and
that could be a portfolio project), but I already
had this file listing shell script. - This wheel already exists
- If the server supports it, files ending in .phps
will show highlighted source (this is an apache
directive) - See also http//www.sitepoint.com/article/highligh
t-source-code-php
30Sockets
31Theory of Sockets
- A socket is a combination of an ip number, a
protocol and a port - Servers listen on a socket, clients will open a
connection to that socket - What gets sent back and forth over the socket
connection varies with the protocol - Generally, when working with a client, you don't
specify the local socket you'll use (the os
handles that), just the remote socket
32An example Web Request
fp fsockopen("www.cs.unc.edu", 80,
errno, errstr, 30) if (!fp) echo
"errstr (errno)ltbr /gt\n" else out
"GET /index.html HTTP/1.1\r\n" out .
"Host www.example.com\r\n" out .
"Connection Close\r\n\r\n" fwrite(fp,
out) while (!feof(fp)) echo
fgets(fp, 128) fclose(fp)
- This is a socket call to a web server
- fp represents a "handle" to the socket
- If the handle is created, you have the socket
11_webconnection.php
33Sending email with PHP
- The unix version of PHP includes support for
sendmailmail ( string to, string subject,
string message) - Since windows doesn't support sendmail, the
windows build of PHP uses an external smtp
server, but uses the same command - But it doesn't work under OSX by default--OS X
uses postfix, and that's off by default - I could have enabled postfix, but I'm a geek...
34Example Protocol SMTP
- By convention, smtp servers listen on ports 25
and 587 - By the rfc, smtp servers communicate in plain
text--you can telnet to an smtp server to send
email - Verbs in smtp include helo, mail from, rcpt to,
data - Use "." to end the body of a message (and this
is one place where a potential for hacking
exists)
35An SMTP call
gilgamesh hays telnet smtp.unc.edu 25 Trying
152.2.1.140... Connected to smtp.unc.edu. Escape
character is ''. 220 smtp.unc.edu ESMTP
UNC-Chapel Hill - ITS Email Tue, 30 Sep 2008
185657 -0400 (EDT) helo gilgamesh 250
smtp.unc.edu Hello gilgamesh.cs.unc.edu
152.2.131.71, pleased to meet you mail
fromhays_at_cs.unc.edu 250 2.1.0 hays_at_cs.unc.edu...
Sender ok rcpt tob_at_unc.edu 250 2.1.5
b_at_unc.edu... Recipient ok data 354 Enter mail,
end with "." on a line by itself Subject
Test To b_at_unc.edu From hays_at_cs.unc.edu Howdy,
this is a test. . 250 2.0.0 m8UMuvtq021026
Message accepted for delivery quit 221 2.0.0
smtp.unc.edu closing connection Connection closed
by foreign host.
36Bits and pieces
- fsockopen opens a connection, and establishes a
point to that connection, with server, port and
timeout specifiable - fwrite (or fputs) and fgets write and read to
that connection, feof tests for end of file
fp fsockopen("www.example.com", 80, errno,
errstr, 30) if (!fp) echo "errstr
(errno)ltbr /gt\n" else out "GET /
HTTP/1.1\r\n" out . "Host
www.example.com\r\n" out . "Connection
Close\r\n\r\n" fwrite(fp, out) while
(!feof(fp)) echo fgets(fp, 128)
fclose(fp) this from php manual
16
37My version of mail
- I didn't want to enable a mail server, so I
hacked an emailer function that - Checks the message for some possible hacks
- Checks the MX record of the target domain
- Opens a connection to an smtp server
- Writes the message to that connection
- Checks the responses for success or failure
38The call
- Call this function with a message id, an array
that contains the tos, a subject line, the
message, the ip of the web server, the smtp
server to use, and the portemailer(rand(10000,
30000), to_list, "hays_at_cs.unc.edu", "Test",
"test test test", _SERVER"REMOTE_ADDR",
"smtp.unc.edu", "587")
39Sanity Checks
- Check for the to list to be an array (coercion
can occur) - Check for embedded addresses and periods
if (!is_array(email_to))
die('Sorry, email_to must be an array')
email_subject str_replace("."," .
",email_subject) email_text
str_replace("."," . ",email_text)
email_subject str_replace("_at_"," AT
",email_subject) email_text
str_replace("_at_"," AT ",email_text)
40Check Email Validity
- list(username,domain) split("_at_",email_from
) - if (!getmxrr(domain,mxrecords))
-
- echo "ltpgtError The email domain for the
\"from\" address, email_from, cannot be
verified.lt/pgt" - echo "ltpgtMail sent from this address may
not be deliverable, and replies may not
work!lt/pgt" - // end of if
-
- y0
- while (email_toy !'')
-
- list(username,domain)
split("_at_",email_toy) - if (!getmxrr(domain,mxrecords))
- echo "ltpgtError The email domain for the
\"to\" address, email_toy cannot be
verified.lt/pgt" - echo "ltpgtMail sent to this address may
not be deliverable!lt/pgt" - // end of if
- y
- // end of while
41Build Messages
- This part builds an array, message, that contains
elements representing each line of the mail
connection we need - We'll walk this array later to send the mail
x0 // Build a helo message, using
the php server's IP name messagex
"HELO " . php_server . "\r\n" x
// Set the from address messagex "MAIL
FROMlt" . email_from . "gt\r\n" x
42Now the from and tos
- y0
- messagex "RCPT TOlt" . email_toy .
"gt\r\n" - to_list email_toy
- x
-
- // Now do a loop to set the rest of the
recipients. - // Pull each address out of the array email_to
array - // and also add that address to the to_list
for inclusion in the headers - y
- while (email_toy ! '')
- messagex "RCPT TOlt" . email_toy
. "gt\r\n" - to_list to_list . ", lt" .
email_toy . "gt" - x
- y
- // end of while
43Start a Data Statement
- Start the data statement // Set the Data
statement messagex "DATA\r\n" x - But the rest of the data statement is messy, it
contains all of the header data you get in an
email message--take a deep breath.
44Email Headers
- Message-ID lt11819_at_152.2.131.71gt
- Date Thu, 13 Sep 2007 162051 -0400
- From hays_at_cs.unc.edu
- To hays_at_cs.unc.edu
- Subject Test
- Content-Type text/plain charsetus-ascii
- Content-Transfer-Encoding 7bit
- X-Mailer Php program running from
wwwx.cs.unc.edu/hays/INLS668/samples/php/function
s/test.php - X-SAV-Scan-By smf-sav v1.4.1 -
http//smfs.sf.net/ - Received-SAV Pass (fafnir.cs.unc.edu sender
hays_at_cs.unc.edu - passed Sender Address Verification Test
- receiverfafnir.cs.unc.edu client-ip152.2.1.139
- envelope-fromlthays_at_cs.unc.edugt
helosmtp.unc.edu - X-Scanned-By MIMEDefang 2.62 on 152.2.129.97
- X-Scanned-By MIMEDefang 2.62 on 152.2.129.90
45Making Email Headers
- messagex "Message-ID lt" .
email_messageid . "_at_" . php_server .
"gt\r\nDate " . date(r) . "\r\nFrom " .
email_from . "\r\nTo " . to_list .
"\r\nSubject " . email_subject .
"\r\nContent-Type text/plain charsetus-ascii\r\
nContent-Transfer-Encoding 7bit\r\nX-Mailer Php
program running from ". _SERVER"SERVER_NAME" .
_SERVER"SCRIPT_NAME" . "\r\n" . email_text .
"\r\n.\r\n" - x
-
- // Build a quit message, this is the last step
when it comes to the connection - messagex "QUIT\r\n"
46Making the Connection
- stream fsockopen(smtp_server, smtp_port,
errno, errstr, 30) - // Check for error messages from the socket
connection. - if (!stream)
- echo "ltpgtI'm sorry, there appears to be a
problem sending email. \n" - echo 'ltpgtPlease report this error. lta
href"mailto' . email_admin .
'"gt' . email_admin . "lt/agtlt/pgt\n" - echo "ltpgtAlso, this error does not mean
that anything else went wrong, it
only indicates \n" - echo "that the email message this program
was trying to send did not go out
successfully. \n" - echo "Anything else you were trying to do
likely was not affected by - this error.lt/pgt\n"
- // end of if
47Peel off the data
- else // Since we got a socket, send the mail
- x0 // reset x for the loop
- while (messagex ! '')
-
- smtp_response fgets(stream, 1024)
// Check for server responses - sleep(1)
-
- fputs(stream, messagex) // Put the
message line up to the server
48Check the Server Responses
- if (substr(smtp_response, 0, 1) gt '3')
-
- echo "ltpgtI'm sorry, there appears to
be a problem sending email. \n" - echo "The particular message I
received waslt/pgt\n" - echo "ltpregt" . smtp_response .
"lt/pregt" -
- // Since there has been a
nontransient error, try to reset and quit
gracefully - fputs(stream, "RSET")
- sleep(1)
- fputs(stream, "QUIT")
- sleep(1)
-
- // Set the next message to null
- // Since email failed we just give up
and this exits the loop for us - message(x 1) ''
- //end of if
49See it in action
- This is in the functions folder
- There are earlier versions for comparison (why
upgrade, and what to what for?)
50Regular Expressions
51Simple regular expression
// Check for malodorous site calls if
(ereg("(//)", _GETname)) //echo "Call to
an external site" email_messageid
date(U) email_to0 "hays_at_cs.unc.edu"
email_subject 'Intrusion Attempt from
frame.php' email_text 'There has been an
intrusion attempt originating from '
. HTTP_SERVER_VARSREMOTE_ADDR email_from
"hays_at_cs.unc.edu" php_server
HTTP_SERVER_VARSHTTP_HOST smtp_server
"smtp.cs.unc.edu" smtp_port "25"
emailer(email_messageid, email_to, email_from,
email_subject, email_text,
php_server, smtp_server,
smtp_port)
52Regular Expressions Syntax
- Two major dialects, posix and perl (we're using
the former) - The basic usage is "match a_string a_blob", where
the string is what you're looking for and the
blob is where it might be - There are a lot of variants that do search and
replace, forward and back, etc., but the basic
function in PHP is ereg()
53ereg() examples
- ereg("fair", blob) looks for the string "fair"
- ereg(0-9, blob) looks for any instance of a
digit from 0 to 9 - ereg(cq, blob) looks for either a c or q (this
is the same as "cq" - ereg((0-3)(fair), blob) looks for any digit
0-3 or the string "fair" (the is an or)
54ereg and Anchors
- ereg((This), blob) looks for This at the
beginning of blob - ereg((This), blob) looks for This at the end of
blob
55More examples
- ereg(7-9.ing, blob) looks for a 7-9 digit,
following by any single character, followed by
the string "ing" (the period is a wildcard) - ereg(rx4,7, blob) looks for an r followed by
4-7 x's (the brackets provide the range on the
prior character - ereg(0-95s, blob) looks for 5 digits each in
the range of 0-9, followed by an s
15_ereg.php
56Escaping ereg Characters
- .()?\ are special characters for regular
expressions, and must be escaped with a backslash - ereg(\_at__at__at_\, blob) looks for '_at__at__at_'
- ereg(",\.\_at__at_, blob) looks for '",._at__at_'
57For the masochistic Some examples of validation
- http//www.ilovejackdaniels.com/php/email-address-
validation/ - http//www.zend.com/codex.php?id371single1
- http//scripts.franciscocharrua.com/verify-email-a
ddress.php
58Regex info
- http//www.tin.org/bin/man.cgi?section7topicreg
ex - http//analyser.oli.tudelft.nl/regex/index.html.en
- http//weblogtoolscollection.com/regex/regex.php
59Sources
- http//www.zend.com/zend/art/intro.php
- http//www.php.net/
- http//hotwired.lycos.com/webmonkey/programming/ph
p/index.html