PDA

View Full Version : How does PHP format echos and emails?



dog
12-15-2006, 03:31 AM
I have just got my first bit of PHP working with a lot of help from thetestingsite.

It sends and email with some information from a form page. Here's a section of the code that I have some questions about:


mail($email, "Password Reminder", "Password reminder:

Username: ".$username."
Password: monkey

Please login at http://torema.com.br/cases.php", "From: noreply@torema.com.br");

This sends an email with a password reminder in the message body.

Originally I tried it like this using the html tag <br> to format the text...

mail($email, "Password Reminder", "Password reminder:<br><br>
Username: ".$username."<br>Password: monkey<br><br>
Please login at http://torema.com.br/cases.php", "From: noreply@torema.com.br");...however the <br> tags showed up in the message body.

Now I'm wondering how to put HTML into a string (terminology?) written by the PHP such as an emails body or an echo.

Thanks,

dog

thetestingsite
12-15-2006, 04:23 AM
A way that I have done it before is by making a variable (lets say $msg), then doing the formatting one of two ways. (Both should work, but I have only worked with the first one personally.



$msg = <<<HERE
This is my formatted
email
text.
HERE;


or



$msg = "This is
my formatted
email text";


Now as far as entering HTML in it, I'm not sure because I haven't really played around with it that much.

Good luck!

dog
12-15-2006, 12:37 PM
Thanks. I was suprised that the line breaks have an effect on the formatting but they do.

Now I'm just really curious about what's going on in PHP when you get it to print text like this.

HTML tags aren't behaving as HTML they're just appearing as plain text and line breaks are effecting the formating! :eek: If the PHP isn't writing HTML what is it doing?

And how do you get PHP to write HTML?

I'm asking all of this regarding what PHP sends for an email body but would also like to know about options for formating what PHP sends in echos.

Are these two completely different things and are there some standard rules that apply to formating text that gets sent by PHP?.

I hope my questions are understandable. I lack a lot of technical terms. :)

boxxertrumps
12-15-2006, 03:38 PM
you'll have to figure out how the email server handles email, sending them in bbcode/html or xml.

mwinter
12-15-2006, 03:48 PM
I was suprised that the line breaks have an effect on the formatting but they do.

Why shouldn't they? The content is just text.



HTML tags aren't behaving as HTML they're just appearing as plain text and line breaks are effecting the formating!

That's because it is plain text.



And how do you get PHP to write HTML?

You need to send a Content-Type header that informs the e-mail client that it's received HTML. However, HTML mail should always be accompanied by a plain text version, and in this case, there seems to be little point in sending both.

Mike

dog
12-15-2006, 04:06 PM
HTML tags aren't behaving as HTML they're just appearing as plain text and line breaks are effecting the formating!
That's because it is plain text.


:p Thanks! That made me laugh at myself for being so slow.

As for the echos. I've been doing some experienting like putting strong tags around the text (example included in code below)) and they seem to behave just like they would in HTML. This is because it is HTML, right?:rolleyes:

Another thing is I'd like to get the echo to appear on the original page.

I'm using a header redirect to return to the original page. See full code below:


<?php


//if "email" is filled out, check for "name"
if (isset($_REQUEST['email']))
{
//if "name" is filled out, send a couple of mails
if (isset($_REQUEST['name']))
{

$email = $_REQUEST['email'] ;
$message = "A password reminder has been sent to <".$_REQUEST['name'].">". $_REQUEST['email'] ;
$username = $_REQUEST['name'];

mail("webmaster@torema.com.br", "Password Reminder Requested",
$message, "From: noreply@torema.com.br");

mail($email, "Password Reminder", "Password reminder:


Username: ".$username."
Password: monkey

Please login at http://torema.com.br/cases.php", "From: noreply@torema.com.br");

header("Refresh: 2; url=password-reminder.php");
echo '<strong>A reminder of the password has been sent to <'.$username.'>'.$email.'.</strong>';

}
else
//if "name" is not filled out, ask them to fill it out
{
echo "please give your name";
}
}
else
//if "email" is not filled out, ask them to fill it out
{
echo "please fill out an email address";
}
?>


How can I change this to get the echo to appear on password-reminder.php?

And does anyone know why the checks for content in the 'name' and 'email' fields aren't working?

Acey99
12-15-2006, 11:53 PM
It's not php or the server.
It's the email program(s)
if you want line feeds in email instead of <br> use \n\r (new line for pc/mac)

yes most email programs read html, but not <br>, dunno why, but they don't.

mwinter
12-16-2006, 03:17 AM
As for the echos. I've been doing some experienting like putting strong tags around the text (example included in code below)) and they seem to behave just like they would in HTML. This is because it is HTML, right?:rolleyes:

Yes. By default, the output from PHP that's sent back to the visitor's browser is "labelled" as text/html.



Another thing is I'd like to get the echo to appear on the original page.

I'm not familiar with how you're organising this. Do you mean that you're submitting the form to a different URI then trying to redirect back to the previous URI? If so, it would easier to submit the form to the same URI and change what's sent to the browser based on whether the visitor is requesting the document for the "first" time, or as a result of the submission.



mail($email, "Password Reminder", "Password reminder:


Username: ".$username."
Password: monkey

Please login at http://torema.com.br/cases.php", "From: noreply@torema.com.br");

The second string literal is somewhat dubious: e-mails have a specific format and new lines must be represented as a carriage return/line feed pair. It would be more robust to use character escapes to represent those characters (string literal split for readability only):



"Password reminder:\r\n\r\n\r\n"
. "Username: {$username}\r\n"
. "Password: monkey\r\n\r\n"
. 'Please log-in at <http://torema.com.br/cases.php>'




header("Refresh: 2; url=password-reminder.php");

"Refresh" is not a defined HTTP header. Whether or not browsers seem to take it to mean "redirect after two seconds", include a link within the document should it fail.



And does anyone know why the checks for content in the 'name' and 'email' fields aren't working?

You aren't checking for content. The isset function only ensures that there is 'name' and 'email' element in the $_REQUEST superglobal, respectively. These are significant tests, but they don't do what you think they do.

To check for actual content, it's best to use regular expressions to match the input against a particular pattern. Unfortunately, the lack of Unicode support in PHP makes proper internationalisation rather more difficult than it needs to be, though versions after 4.4.0 and 5.1.0 attenuate this somewhat by providing Unicode character category escape sequences.

I prefer not to use the $_REQUEST superglobal if source of the data is known. That is, use $_POST for POST data, and $_GET for values in the query string.

Mike

dog
12-16-2006, 09:17 PM
Thanks for the tips about formating the PHP output.

What's the difference in using \r\n rather than just \n? I've not come across \r before.


"Refresh" is not a defined HTTP header. Whether or not browsers seem to take it to mean "redirect after two seconds", include a link within the document should it fail.


I'm now using this instead. I found it on the section about header (http://www.php.net/manual/en/function.header.php) on php.net (http://www.php.net)


// redirect to a different page in the current directory that was requested
$host = $_SERVER['HTTP_HOST'];
$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
$extra = 'form.php';

header("Location: http://$host$uri/$extra");
exit;



The isset function only ensures that there is 'name' and 'email' element in the $_REQUEST superglobal, respectively. These are significant tests, but they don't do what you think they do.

I've taken these tests out now. Could this cause significant problems?


To check for actual content, it's best to use regular expressions to match the input against a particular pattern.

I'm using this now:



//if the email or name fields are blank, redirect to an error message
if ($_REQUEST['email']=="" or $_REQUEST['name'] =="")
{...redirect...}

//otherwise, send the mail
else
{...mail...}



I prefer not to use the $_REQUEST superglobal if source of the data is known. That is, use $_POST for POST data, and $_GET for values in the query string.

So, for example you'd suggest I change this:



$name = $_REQUEST['name'];
$company = $_REQUEST['company'];
$position = $_REQUEST['position'];


to this:



$name = $_GET['name'];
$company = $_GET['company'];
$position = $_GET['position'];


?

Can you give an example of when I might us $_POST?

mwinter
12-17-2006, 03:11 PM
What's the difference in using \r\n rather than just \n?

The former outputs the characters, carriage return (CR) followed by line feed (LF). As I wrote previously, line breaks require both characters: a line feed on its own is illegal.



Messages are divided into lines of characters. A line is a series of characters that is delimited with the two characters carriage-return and line-feed; that is, the carriage return (CR) character (ASCII value 13) followed immediately by the line feed (LF) character (ASCII value 10). (The carriage-return/line-feed pair is usually written in this document as "CRLF".)


2.1 General Description, Lexical Analysis of Messages, RFC 2822 Internet Message Format. (http://www.ietf.org/rfc/rfc2822.txt)





The body of a message is simply lines of US-ASCII characters. The only two limitations on the body are as follows:


CR and LF MUST only occur together as CRLF; they MUST NOT appear independently in the body.
Lines of characters in the body MUST be limited to 998 characters, and SHOULD be limited to 78 characters, excluding the CRLF.



2.3 Body, Lexical Analysis of Messages, RFC 2822 Internet Message Format (http://www.ietf.org/rfc/rfc2822.txt).





I've not come across \r before.

It's the escape sequence used in string literals within various languages to refer to the carriage return character. See the section on double-quoted strings (http://uk.php.net/manual/en/language.types.string.php#language.types.string.syntax.double) in the PHP manual (http://uk.php.net/manual/en/index.php).





// redirect to a different page in the current directory that was requested
$host = $_SERVER['HTTP_HOST'];
$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
$extra = 'form.php';

header("Location: http://$host$uri/$extra");
exit;


Even when using the Location header - which usually generates a 302 Found response - a short HTML message should be included.



The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).


10.3.3 302 Found, RFC 2616 Hypertext Transfer Protocol HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)








The isset function only ensures that there is 'name' and 'email' element in the $_REQUEST superglobal, respectively. These are significant tests, but they don't do what you think they do.

I've taken these tests out now. Could this cause significant problems?

If the elements don't exist within the super-global array, notices will be sent to the error log. Test that the elements exist first, then assess the values.






//if the email or name fields are blank, redirect to an error message
if ($_REQUEST['email']=="" or $_REQUEST['name'] =="")
{...redirect...}

//otherwise, send the mail
else
{...mail...}


And if the e-mail address isn't actually an e-mail address, or the name is just white space?




So, for example you'd suggest I change this:



$name = $_REQUEST['name'];
$company = $_REQUEST['company'];
$position = $_REQUEST['position'];


to this:



$name = $_GET['name'];
$company = $_GET['company'];
$position = $_GET['position'];


?

If the form is submitted using the GET method, yes. However, it should be sent using the POST submission method.




Can you give an example of when I might us $_POST?



<form ... method="post">

Mike

dog
12-17-2006, 03:54 PM
Even when using the Location header - which usually generates a 302 Found response - a short HTML message should be included.

The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s).


10.3.3 302 Found, RFC 2616 Hypertext Transfer Protocol HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)



Ok, I'll put one in if you can tell me how. Please remember I'm really new to PHP and haven't had much time free to do much reading up.



If the elements don't exist within the super-global array, notices will be sent to the error log. Test that the elements exist first, then assess the values.

I'm testing their existance manually, I think. I'm testing over and over again and if they don't exist in the form I'd know about it. As I'm sure that they exist is it neccessary to test for them using the PHP code? I don't understand the advantages to doing this in my current situation. Are there any?


And if the e-mail address isn't actually an e-mail address, or the name is just white space?
Ok, so what alternative should I know about? I've got very limited experience of PHP and until I come across other options I'm going to make do with what I've got. I don't know how to validate a name or email any better than this.

I do however have more questions:

You suggested this script:

...(string literal split for readability only)

"Password reminder:\r\n\r\n\r\n"
. "Username: {$username}\r\n"
. "Password: monkey\r\n\r\n"
. 'Please log-in at <http://torema.com.br/cases.php>'
I didn't really understand what you meant by string literal split. I've tried closing and opening the string with quotation marks (') but ever time I do I cause errors.

For example, I'm trying to write a string (terminology?) called $message1 and I find it convenient to split it into several lines but in doing so I'm causing errors. Anyone who likes could tell me what's causing them in the following code. All of this code is meant to be a definition of $message1.


$message1 = 'E-mail do Site da Pagina: Fale Conosco\r\n\r\n'

'Nome: {$name}\r\n'
'Cargo: {$company}\r\n'
'Empresa: {$company}\r\n'
'Cidade: {$city} / {$state}'

//if the country is anything other than Brasil state it
if ($country=='Brasil')
{ echo '\r\n\r\n';}
//otherwise leave it out
else
{ echo '\r\n\r\n';}

'Intressa: {$interest}\r\n'
//if there is a message echo it
if ($message=='')
{ echo '\r\n';}
else
{ echo 'Mensagem: '.$message.' \r\n';}

'Deseja Contato Por: {$contactType}\r\n'
'Horario de Contato: {$timeTable}\r\n\r\n'

'Fone: {$tel}\r\n'
'E-mail: {$email}\r\n'
'Site: www.{$site}\r\n';

You'll notice there is another issue involved in this script. I"ve got no idea if I can use if-else statements and echos like this. I hope they at least give the impression of what it is I'm trying to do.

Thanks to all.

dog

mwinter
12-18-2006, 03:40 AM
Even when using the Location header - which usually generates a 302 Found response - a short HTML message should be included.

Ok, I'll put one in if you can tell me how.

Perhaps the easiest approach in the short term is to drop out of PHP parsing mode after sending the header, output the HTML, then re-enter PHP mode and call the exit function:



$host = $_SERVER['HTTP_HOST'];
$path = rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/form.php';
$uri = 'http://' . $host . $path;

header('Location: ' . $uri);
/* Drop out of PHP */
?>
<!DOCTYPE ...>

<!-- ... -->
<a href="<?php echo $uri ?>">...</a>
<!-- ... -->
<?php
exit();

Parts have clearly been omitted, but after filling out the HTML part in the middle, you should be able to drop this in place of the code you posted previously that set the Location header.




I'm testing their existance manually, I think.

The isset function tests for existence, though some array functions could be used, too.




I'm testing over and over again and if they don't exist in the form I'd know about it. As I'm sure that they exist is it neccessary to test for them using the PHP code?

Yes, because you cannot be sure that they exist. Never trust user input. Don't assume it will always be there, and never assume that it will be what it's supposed to be. Such assumptions lead to broken code and security issues.




I don't understand the advantages to doing this in my current situation. Are there any?

One should ensure that elements exist in arrays before attempting to access them. It's simply how things should be done. At the very least, it avoids the unnecessary generation of log entries.






And if the e-mail address isn't actually an e-mail address, or the name is just white space?

Ok, so what alternative should I know about?

As I wrote previously: regular expressions. You can find code for validating e-mail addresses on the Web. Google is your friend. Simply ensuring that input contains more than white space can be implemented with:



if (!isset($_POST['name'])
|| !preg_match('/\\S+/', ($name = trim($_POST['name'])))) {
/* Name missing or only white space */
}
/* The name can be referred to by $name */

If the input comes from query string data, substitute $_POST for $_GET.




You suggested this script:


...(string literal split for readability only)



"Password reminder:\r\n\r\n\r\n"
. "Username: {$username}\r\n"
. "Password: monkey\r\n\r\n"
. 'Please log-in at <http://torema.com.br/cases.php>'


I didn't really understand what you meant by string literal split.

Not much: if the string was written as one long literal, it would have been (more) difficult to read and would have caused horizontal scrolling within the code box. Instead, I split it into several - four - smaller literals and concatenated them using the string concatenation operator (.).



For example, I'm trying to write a string (terminology?)

To define a few terms, a literal is a representation of some sort of value in source code. Each basic data type - primitive - has a literal form.

Strings are sequences of characters: text. They are usually included in quotes (' or "), though another form does exist. Variable expansion - replacing a variable name with its value - doesn't occur in the single-quote form of a string literal. That is,



$foo = 'bar';
echo 'Value: $foo';

would output "Value: $foo" not "Value: bar". The range of escape sequences is also reduced in the single-quote form: only \\ (single backslash) and \' (single quote) are processed.

I generally use the single-quote form whenever possible as the PHP parser will process them slightly quicker due to their simplicity, but it doesn't really matter that much.

Numbers and booleans (true/false) also have literals forms:



$hundred = 100;
$third = 0.66666;
$c = 3e8; // 300000000
$isReady = true;





called $message1 and I find it convenient to split it into several lines but in doing so I'm causing errors. Anyone who likes could tell me what's causing them in the following code. All of this code is meant to be a definition of $message1.


$message1 = 'E-mail do Site da Pagina: Fale Conosco\r\n\r\n'

'Nome: {$name}\r\n'
'Cargo: {$company}\r\n'
'Empresa: {$company}\r\n'
'Cidade: {$city} / {$state}'

//if the country is anything other than Brasil state it
if ($country=='Brasil')
{ echo '\r\n\r\n';}
//otherwise leave it out
else
{ echo '\r\n\r\n';}

'Intressa: {$interest}\r\n'
//if there is a message echo it
if ($message=='')
{ echo '\r\n';}
else
{ echo 'Mensagem: '.$message.' \r\n';}

'Deseja Contato Por: {$contactType}\r\n'
'Horario de Contato: {$timeTable}\r\n\r\n'

'Fone: {$tel}\r\n'
'E-mail: {$email}\r\n'
'Site: www.{$site}\r\n';

You aren't concatenating each literal with the next. A full stop (period) is the operator used here.



/* Notice the change in quotation mark. This is necessary to replace escape
* sequences with the respective character, and to perform variable expansion.
*/
$message1 = "E-mail do Site da Pagina: Fale Conosco\r\n\r\n"
. "Nome: {$name}\r\n"
. "Cargo: {$company}\r\n"
. "Empresa: {$company}\r\n"
. "Cidade: {$city} / {$state}";

/* If country is not Brazil, include it in the message. */
if ($country != 'Brazil') {
$message1 .= "Country: {$country}";
}
$message1 .= "\r\n\r\n"
. "Intressa: {$interest}\r\n";

/* If there is a user-defined message, add it. */
if ($message != '') {
$message1 .= "Mensagem: {$message}";
}
$message1 .= "\r\n"
. "Deseja Contato Por: {$contactType}\r\n"
. "Horario de Contato: {$timeTable}\r\n\r\n"
. "Fone: {$tel}\r\n"
. "E-mail: {$email}\r\n"
. "Site: www.{$site}\r\n";

You might want to choose a better name than $message1. :)

There's another way to write the code above without if statements. Instead, one could use the conditional operator, which is almost like an if statement for use in expressions, but we can leave that for another time.




You'll notice there is another issue involved in this script. I"ve got no idea if I can use if-else statements and echos like this.

The if statements are fine, though as you can see, it interrupts the process of building the string a little. As for the echo construct, this is meant for sending the value of string as output. For server-side PHP, this means transmitting it to the browser.




I hope they at least give the impression of what it is I'm trying to do.

I believe so. :)

Mike

dog
12-18-2006, 04:25 AM
Hi Mike,

Thanks for all the info.

I'll update my script and post up the things I'm not sure of afterwards.

The script is working ok at the moment, as in it does most of the things I want it to but I've hit upon one major problem.

I think this comes back to a point you made previously:



I'm not familiar with how you're organising this. Do you mean that you're submitting the form to a different URI then trying to redirect back to the previous URI? If so, it would easier to submit the form to the same URI and change what's sent to the browser based on whether the visitor is requesting the document for the "first" time, or as a result of the submission.


I didn't understand what you meant by change what's sent to the browser based on whether the vistor is requesting the document for the "first" time, or as a result of the submisson. Would this be the difference between form.php and form.php?sent=yes?

Currently I am submitting the form to a different URI. How would the form be submitted if it didn't go to a different URI?

I think the problem I'm having could be solved somehow by staying at the same URI. What is occuring is that if the form has not been fully filled out the user is returned to the form to fill out all of the required fields, the page is reloaded and the form is cleared so all of the work they've already done in filling out some of the fields is lost.

I don't know what I can do about this. Any suggestions?

mwinter
12-21-2006, 03:19 AM
I didn't understand what you meant by change what's sent to the browser based on whether the vistor is requesting the document for the "first" time, or as a result of the submisson. Would this be the difference between form.php and form.php?sent=yes?

Not exactly, though that's a rudimentary way of doing it.



Currently I am submitting the form to a different URI. How would the form be submitted if it didn't go to a different URI?

Requesting a document and submitting a form are the same thing. The only difference between the two is that the latter sends some extra data, either in the query string, or the body of the request (which is usually empty).

When submitting form data to the same document that contained the form in the first place, the first step is to distinguish between submission and a normal request. Based on that test, the script can vary what information it sends back to the browser.



I think the problem I'm having could be solved somehow by staying at the same URI. What is occuring is that if the form has not been fully filled out the user is returned to the form to fill out all of the required fields, the page is reloaded and the form is cleared so all of the work they've already done in filling out some of the fields is lost.

I don't know what I can do about this. Any suggestions?

To retain typed values, the value attribute of each control can be initialised to the submitted value. Similarly, the selected attribute can be added to items that were selected in a list, and the checked attribute to chosen radio buttons and checkboxes. The isset function can be used to indicate whether a value was sent. For example,



<input name="last-name" type="text" value="<?php
if (isset($_POST['last-name'])) {
echo $_POST['last-name'];
}
?>">

This would work whether the form was submitted, but perhaps with errors, or when the form is requested for the first time. In the latter case, the isset test would fail and nothing would be written into the value attribute.

The basic process is:

If no form input received, go to 6.
If any required input missing, or any received input is invalid, go to 6.
Process input data.
Either redirect or return response (results of step 3, for example).
Go to 7.
Return form, including any received input, and error messages for invalid data.
Exit.

Note that step 1 isn't an error condition, unlike step 2.

There are different ways of implementing this, varying in sophistication. The structure of the form and the way in which error messages are to be presented also greatly affect implementation choices.

My Internet connection is very intermittent at the moment. If you need urgent help over the next few days whilst my provider gets its act together, someone else will probably have to give it.

Mike