600 likes | 730 Views
More PHP. What we'll cover. A short history of php Parsing Variables Arrays Operators Control Structures Forms Functions Accessing the shell Sockets Regular Expressions. Forms. Obviously, one of the things you need to handle are forms
E N D
What we'll cover • A short history of php • Parsing • Variables • Arrays • Operators • Control Structures • Forms • Functions • Accessing the shell • Sockets • Regular Expressions
Forms • 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
Simple Form <form action="./simpleform.php" method="GET" name="choices"> <p>What is the meaning of life?</p> <p><textarea rows="1" cols="50" wrap="virtual" name = "answer"></textarea></p> <p>What is your name?</p> <p><textarea rows="1" cols="50" wrap="virtual" name = "name"></textarea></p> <p>Where is your car?</p> <p><textarea rows="1" cols="50" wrap="virtual" name = "car"></textarea></p> <p><input type="submit" value="Submit" align="middle" /></p> </form> simpleform/simpleform.html
Simple Form php <?php if (count($_GET) < 1) { echo "no GETs!"; } else { echo "Here is the GET array:"; echo "<pre>"; print_r($_GET); echo "</pre>"; } ?> • 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
Simple 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…
simpleform_self.php <?php if (!$_GET) { echo "<form action=\"" . $_SERVER["PHP_SELF"] . "\" method=\"GET\" name=\"choices\">\n"; echo "<p>What is the meaning of life?</p> <p><textarea rows=\"1\" cols=\"50\" wrap=\"virtual\" name = \"answer\"></textarea></p>\n"; echo "<p>What is your name?</p> <p><textarea rows=\"1\" cols=\"50\" wrap=\"virtual\" name = \"name\"></textarea></p>\n"; echo "<p>Where is your car?</p> <p><textarea rows=\"1\" cols=\"50\" wrap=\"virtual\" name = \"car\"></textarea></p>\n"; echo '<input type="hidden" value = "111" name = "key_number" />' ; echo '<p><input type="submit" value="Submit" align="middle" /></p>'; }
simpleform_self.php else { echo "Here is the GET array:"; echo "<pre>"; print_r($_GET); echo "</pre>"; } ?> </form> </body> </html> • 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?
Sessions • 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
simpleform_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)
simpleform_self_session.php <?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_counter'); // session counter is used to // track where we are in // a session ?>
simpleform_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; }
simpleform_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 "<form action=\"" . $_SERVER["PHP_SELF"] . "\" method=\"GET\" name=\"choices\">\n"; echo "<p>What is the meaning of life?</p><p><textarea rows=\"1\" cols=\"50\" wrap=\"virtual\" name = \"answer\"></textarea></p>\n"; … echo '<input type="hidden" value = "' . $_SESSION['session_key'] . '" name = "key_number" />'; echo '</form>'; $_SESSION['counter'] = 1; }
simpleform_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 "<pre>"; print_r($_GET); echo "</pre>";
simpleform_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 "<form action=\"" . $_SERVER["PHP_SELF"] . "\" method=\"GET\" name=\"choices\">\n"; echo '<input type="hidden" value = "' . $_SESSION['session_key'] . '" name = "key_number" />'; echo '<p>Are these ok?</p>'; echo '<input type="radio" value="yes" name = "yes_no" />yes <input type="radio" value="no" name = "yes_no" />no'; echo '<p><input type="submit" value="Submit" align="middle" /></p>'; echo '</form>'; $_SESSION['counter'] = 2; }
simpleform_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 '<p>Cool, everything\'s copacetic.</p>'; // Here you'd do the Good Thing, whatever that is. // Destroy the session session_unset(); session_destroy(); }
simpleform_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 '<p>Oops, sorry, <a href="./simpleform_self_session.php"> try again?</a></p>'; // Here you'd do the Bad Thing, whatever that is // Destroy the session session_unset(); session_destroy(); }
simpleform_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 '<p>I\'m sorry, I found a problem, perhaps you didn\'t fill in all of the boxes. <a href="./simpleform_self_session.php"> Continue?</a></p>'; // Destroy the session session_unset(); session_destroy(); }
User 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)
Passing 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 "<p>$wrapped_item</p>"; } • 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 "<h" . $level . ">$wrapped_item</h" . $level. ">\n"; } 12
Scope • 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
Functions 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(); ?> this example is 17.3 from the php manual, up as 14_functions.php See also 15_function_scope.php
Functions and Default Values function good_echo_h($wrapped_item, $level="2") { echo "<h" . $level . ">$wrapped_item</h" . $level. ">\n"; } function bad_echo_h($level="2", $wrapped_item) { echo "<h" . $level . ">$wrapped_item</h" . $level. ">\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
An 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…
aform • 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
aform • 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
Back 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
10_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/highlight-source-code-php
Theory 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
An example: Web Request $fp = fsockopen("www.cs.unc.edu", 80, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\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
Sending email with PHP • The unix version of PHP includes support for sendmail:mail ( 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...
Example 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)
An 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 18:56:57 -0400 (EDT) helo gilgamesh 250 smtp.unc.edu Hello gilgamesh.cs.unc.edu [152.2.131.71], pleased to meet you mail from:hays@cs.unc.edu 250 2.1.0 hays@cs.unc.edu... Sender ok rcpt to:b@unc.edu 250 2.1.5 b@unc.edu... Recipient ok data 354 Enter mail, end with "." on a line by itself Subject: Test To: b@unc.edu From: hays@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.
Bits 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)<br />\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
My 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
The 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 port:emailer(rand(10000, 30000), $to_list, "hays@cs.unc.edu", "Test", "test test test", $_SERVER["REMOTE_ADDR"], "smtp.unc.edu", "587");
Sanity 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 ",$email_subject); $email_text = str_replace("@"," AT ",$email_text);
Check Email Validity list($username,$domain) = split("@",$email_from); if (!getmxrr($domain,$mxrecords)) { echo "<p>Error: The email domain for the \"from\" address, $email_from, cannot be verified.</p>"; echo "<p>Mail sent from this address may not be deliverable, and replies may not work!</p>"; } // end of if $y=0; while ($email_to[$y] !='') { list($username,$domain) = split("@",$email_to[$y]); if (!getmxrr($domain,$mxrecords)) { echo "<p>Error: The email domain for the \"to\" address, $email_to[$y] cannot be verified.</p>"; echo "<p>Mail sent to this address may not be deliverable!</p>"; } // end of if $y++; } // end of while
Build 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 $x=0; // Build a helo message, using the php server's IP name $message[$x] = "HELO " . $php_server . "\r\n"; $x++; // Set the from address $message[$x] = "MAIL FROM:<" . $email_from . ">\r\n"; $x++;
Now the from and tos $y=0; $message[$x] = "RCPT TO:<" . $email_to[$y] . ">\r\n"; $to_list = $email_to[$y]; $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_to[$y] != '') { $message[$x] = "RCPT TO:<" . $email_to[$y] . ">\r\n"; $to_list = $to_list . ", <" . $email_to[$y] . ">"; $x++; $y++; } // end of while
Start a Data Statement • Start the data statement // Set the Data statement $message[$x] = "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….
Email Headers Message-ID: <11819@152.2.131.71> Date: Thu, 13 Sep 2007 16:20:51 -0400 From: hays@cs.unc.edu To: hays@cs.unc.edu Subject: Test Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Mailer: Php program running from wwwx.cs.unc.edu/~hays/INLS668/samples/php/functions/test.php X-SAV-Scan-By: smf-sav v1.4.1 - http://smfs.sf.net/ Received-SAV: Pass (fafnir.cs.unc.edu: sender hays@cs.unc.edu passed Sender Address Verification Test receiver=fafnir.cs.unc.edu; client-ip=152.2.1.139 envelope-from=<hays@cs.unc.edu>; helo=smtp.unc.edu; X-Scanned-By: MIMEDefang 2.62 on 152.2.129.97 X-Scanned-By: MIMEDefang 2.62 on 152.2.129.90
Making Email Headers $message[$x] = "Message-ID: <" . $email_messageid . "@" . $php_server . ">\r\nDate: " . date(r) . "\r\nFrom: " . $email_from . "\r\nTo: " . $to_list . "\r\nSubject: " . $email_subject . "\r\nContent-Type: text/plain; charset=us-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 $message[$x] = "QUIT\r\n";
Making the Connection $stream = fsockopen($smtp_server, $smtp_port, $errno, $errstr, 30); // Check for error messages from the socket connection. if (!$stream) { echo "<p>I'm sorry, there appears to be a problem sending email. \n"; echo '<p>Please report this error. <a href="mailto:' . $email_admin . '">' . $email_admin . "</a></p>\n"; echo "<p>Also, 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.</p>\n"; } // end of if
Peel off the data else { // Since we got a socket, send the mail $x=0; // reset x for the loop while ($message[$x] != '') { $smtp_response = fgets($stream, 1024); // Check for server responses sleep(1); fputs($stream, $message[$x]); // Put the message line up to the server
Check the Server Responses if (substr($smtp_response, 0, 1) > '3') { echo "<p>I'm sorry, there appears to be a problem sending email. \n"; echo "The particular message I received was:</p>\n"; echo "<pre>" . $smtp_response . "</pre>"; // 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
See it in action • This is in the functions folder • There are earlier versions for comparison (why upgrade, and what to what for?)