PHP server-side validation regular expression match - php

I have the following part of a validation script:
$invalidEmailError .= "<br/>» You did not enter a valid E-mail address";
$match = "/\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b/";
That's the expression, here is the validation:
if ( !(preg_match($match,$email)) ) {
$errors .= $invalidEmailError; // checks validity of email
}
I think that's enough info, let me know if more is needed.
Basically, what happens is the message "You did not enter a valid E-mail address" gets echoed no matter what. Whether a correct email address or an incorrect email address is entered.
Does anyone have any idea or a clue as to why?
EDIT: I'm running this on localhost (using Apache), could that be the reason as to why the preg_match ain't working?
Thanks!
Amit

Your regex only includes [A-Z], not [a-z]. Try
$match = "/\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b/i";
to make the regex case-insensitive.
You can test this live on http://regexpal.com.
However, I'd advise you to try one of the expressions on the page mentioned by strager: http://fightingforalostcause.net/misc/2006/compare-email-regex.php. They have been perfected over time and will probably behave better. But Gmail users will be satisfied with yours, since they'll be able to use plus aliases which are rejected incorrectly by many validators.

You likely got the regular expression you're using from regular-expressions.info. On that page, the author states (emphasis added):
If you want to use the regular expression above, there's two things you need to understand. First, long regexes make it difficult to nicely format paragraphs. So I didn't include a-z in any of the three character classes. This regex is intended to be used with your regex engine's "case insensitive" option turned on. (You'd be surprised how many "bug" reports I get about that.) Second, the above regex is delimited with word boundaries, which makes it suitable for extracting email addresses from files or larger blocks of text. If you want to check whether the user typed in a valid email address, replace the word boundaries with start-of-string and end-of-string anchors, like this: ^[A-Z0-9._%+-]+#[A-Z0-9.-]+.[A-Z]{2,4}$.
To solve this problem, add the i PCRE flag after your regular expression.

You can always try debugging your regex using a simpler tool (I'm quite fond of using Notepad++ for this purpose) and performing iterative tests - ie. making the expression more/less complicated and seeing if that fixes/breaks things.

Related

How to validate email address and website in commnet

I want to validate email address and website in comment box. When someone writes comment in comment box and after submission check if email address or website found in comment remove that email and address.
I have put below regular expression for email.
"/(?:[a-z0-9!#$%&'*+=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/"
above expression validates email address but I want to validate like email[at]email[dot]com, email{at}email{dot}com, email(at)email(dot)com
Same for website validation I used below expression
"/((((http|https|ftp|ftps)\:\/\/)|www\.)[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,4}(\/\S*)?)/"
But I want to validate website like website[dot]com, www[dot]website[dot]com
Basically what you need to do is, where you have the validation of # and . character in email or . in weburl, you need to enhance your regex and put the alternatives to # character as you are expecting. So,
# should be written as (?:#|[[({]at[\]})])
And,
\. should be written as (?:\.|[[{(]dot[\]})])
wherever you have them in your regex and then it will also filter those strings as well.
Here is a modified regex for email.
(?:[a-z0-9!#$%&'*+=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")(?:#|[[({]at[\]})])(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.|[[{(]dot[\]})]))+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
Regex Demo for email
Same way you can replace . from your website regex and your modified regex becomes this,
(?:(?:(?:http|https|ftp|ftps)\:\/\/)|www(?:\.|[[{(]dot[\]})]))(?:[a-zA-Z0-9.-]|[[{(]dot[\]})])+(?:\.|[[{(]dot[\]})])[a-zA-Z]{2,4}(\/\S*)?
Regex Demo for web url
Now besides matching of [dot], {dot} and (dot), the regex will also match [dot} and similar and as you are trying to detect such strings further, hence matching these strings will be an added advantage, rather than a problem unless the context was otherwise.

How to remove offensive words from post by php?

Assume "xyza" is a bad word. I'm using following method to replace offensive words-
$text = str_replace("x***","(Offensive words detected & removed!)",$text);
This code will replace xyza into "(Offensive words detected & removed!)".
But problem is "Case" if someone type XYZA my code can't detect it. How to solve it?
No matter what you do, users will find ways to get around your filters. They will use unicode characters (аss, for example, uses a Cyrillic а and will not get captured by any of the regex solutions). They will use spaces, dollar signs, asterisks, whatever you haven't managed to catch yet.
If family-friendliness is essential to your application, have a person review the content before it goes live. Otherwise, add a flag feature so other people can flag offensive content. Better yet, use some sort of machine learning or Bayesian filter to automatically flag potentially offensive posts and have humans check them out manually. People read human languages better than computers.
The problem with whitelists/blacklists is—as other users have pointed out—your users will make it their priority to find ways around your filter for satisfaction rather than using your website for what it was intended for, whatever that may be.
One approach would be to use Google’s undocumented profanity API it created for its “What Do You Love?” website. If you get a response of true then just give the user a message saying their post couldn’t be submitted due to detected profanity.
You could approach this as follows:
<?php
if (isset($_POST['submit'])) {
$result = json_decode(file_get_contents(sprintf('http://www.wdyl.com/profanity?q=%s', urlencode($_POST['comments']))));
if ($result->response == true) {
// profanity detected
}
else {
// save comments to database as normal
}
}
Other answers and comments say that programming is not the best solution to this problem. I agree with them. Those answers should be moved to Moderators - Stack Exchange or Webmasters - Stack Exchange.
Since this is stackoverflow, my answer is going to be based on computer programming.
If you want to use str_replace, do something like this.
For the sake of this post, since some people are offended by actual cusswords, let's pretend that these are bad words:
'fug', 'schnitt', 'dam'.
$text = str_ireplace(" fug ","(Offensive words detected & removed!)",$text);
Notice, it's str_ireplace not str_replace. The i is for "case insensitive".
But that will erroneously match "fuggedaboudit," for example.
If you want to do a more reliable job, you need to use regex.
$bad_text = "Fug dis schnitt, because a schnitter never dam wins a fuggin schnitting darn";
$hit_words = array("fug","schnitt","dam"); // these words are 'hits' that we need to replace. hit words...
array_walk($hit_words, function(&$value, $key) { // this prepares the regex, requires PHP 5.3+ I think.
$value = '~\b' . preg_quote( $value ,'~') . '\b~i'; // \b means word boundary, like space, line-break, period, dash, and many others. Prevends "refudgee" from being matched when searching for "fudge"
});
/*print_r($bad_words);*/
$good_words = array("fudge","shoot","dang");
$good_text = preg_replace($hit_words,$good_words,$bad_text); // does all search/replace actions at once
echo '<br />' . $good_text . '<br />';
That will do all your search/replacements at once. The two arrays should contain the same number of elements, matching up searches and replace terms. It will not match parts of words, only whole words. And of course, determined cussers will find ways of getting their swearing onto your website. But it will stop lazy cussers.
I've decided to add some links to sites that obviously use programming to do a first run through removing profanity. I'll add more as I come across them. Other than yahoo:
1.) Dell.com - replace matching words with <profanity deleted>.
http://en.community.dell.com/support-forums/peripherals/f/3529/t/19502072.aspx
2.) Watson, the supercomputer, apparently developed a cursing problem. How do you tell the difference between cursing and slang? Apparently, it's so hard that the researchers just decided to purge it all. But they could have just used a list of curse words ( exact matching is a subset of regex, I would say) and forbidden their use. That's kind of how it works in real life, anyway.
Watson develops a profanity problem
3.) Content Compliance section of Gmail custom settings in Apps for Business:
Add expressions that describe the content you want to search for in each message
The "Expresssions" used can be of several types, including "Advanced content match", which, among other things, allows you to choose "Match type" options very similar to what you'd have in an excel filter: Starts with, Ends with, Contains, Not contains, Equals, Is Empty, all of which presumably use Regex. But wait, there's more: Matches regex, Not matches regex, Matches any word, Matches all words. So, the mighty Google implements regex filtering options for its business users. Why would it do that, when regex is supposedly so ineffective? Because it actually is effective enough. It is a simple, fast, programming solution that will only fail when people are hell-bent on circumventing it.
Besides that list, I wonder if anyone else has noticed the similarity between weeding out profanity and filtering out spam. Clearly, regex has uses in both arenas but nitpickers who learned by rote that "all regex is bad" will always downvote any answer to any question if regex is even mentioned.
Try googling "how spam filters work". You'll get results like this one that covers spam assassin:
http://www.seas.upenn.edu/cets/answers/spamblock-filter.html
Another example where I'm sure regex is used is when communicating via Amazon.com's Amazon Marketplace. You receive emails at your usual email address. So, naturally, when responding to a seller, your email program will include all kinds of sender information, like your email address, cc email addresses, and any you enter into the body. But Amazon.com strips these out "for your protection." Can I find a way around this regex? Probably, but it would take more trouble than it's worth and is therefore effective to a degree. They also keep the emails for 2 years, presumably so that a human can go over them in case of any fraud claims.
SpamAssassin also looks at the subject and body of the message for the same sort of things that a person notices when a message "looks like spam". It searches for strings like "viagra", "buy now", "lowest prices", "click here", etc. It also looks for flashy HTML such as large fonts, blinking text, bright colors, etc.
Regex is not mentioned, but I'm sure it's in use.
Use str_ireplace function that Case-insensitive version of str_replace()
$text = str_ireplace("flip","(Offensive words detected & removed!)", $text);
Use 'str_ireplace' to replace any case sensitive strings
Probable, this will help you
$text = 'contains offensive_word .... so on';
$array = array(
'offensive_word' => '****',
'offensive_word2' => '****',
'offensive_word3' => '****',
//.....
);
$text = str_ireplace(array_keys($array),array_values($array), $text);
echo $text;
You should use regex replacement and need to add the i flag to the end of your regex so it searches your text regardless of case. so..
$text = preg_replace("/xyza/i","(Offensive words detected & removed!)", $text);
str_ireplace can also be used if you don't need complex regex rules.
$text = str_ireplace("xyza","(Offensive words detected & removed!)", $text);
In fact, the latter is the preferred way as it's faster than regex manipulation. From PHP docs:
If you don't need fancy replacing rules, you should generally use this function instead of preg_replace() with the i modifier.
BUT, as the commenter pointed out, simple string/regex replacements can break your strings if the substring you're replacing appears as part of another non-offensive word. For this, you could either use word boundaries in your regexes or replace only those words that can't be part of other strings (e.g. the word xyza).

Optimize regexp to catch email signatures

I have a PHP script that mirror my mailing list to web-based forum, in order to make the forum import look as nice as possible, I use regular expression to catch email signatures & style them appropriately. The signature formats that I'm catching with the regex are:
This is my message...
--
My signature
TheDude.
And
This is my message...
---------------
My signature
TheDude.
Right now I'm using this regex:
$message = preg_replace('/\s*(.+)(\s*[\r\n]-{2,}\s+.*)/s', '$1<span class="msg_footer">$2</span>', $message);
It works, my but after some quick tests, I realized that this regex is really slow.
I'm not that good in regex, can someone please take a look at the regex & tell me how to optimize it & make it fast?
You are using regular expressions to handle the whole message, which is bound to be slow. A better alternative would be to use proper programming logic to process the message. For instance, go through the message line by line and test for each line whether it matches your "start of signature" regex. If not, add it to the array or string holding the actual message. If it does match, add the rest of the message to the footer.
You might also want to start from the bottom instead of the top, if you think that your users will use lines matching your regex in the middle of the message.
Assuming that a signature starts with at least two - at the beginning of line and ends with either \r\n, \r or \n one or more times, try this:
$message = preg_replace(
'/^(-{2,})(?=(?:\r?\n|\r)+)/m',
'<span class="msg_footer">$1</span>',
$message
);

preg_match for the email validation I want but somehow I don't know where I messed up

oh eh...ya...lots commented there are lots email validation can be used but just that for this one I have to do it like what is mentioned below that's why....
I need to validate email like this
alphanumeric characters followed by # followed by alphanumeric characters followed by . followed by 2 – 4 more alphanumeric characters
this is what I have done but somehow I know it's the last part after . I messed up but I couldn't find where I messed up....
preg_match("/^([0-9]|[a-z])([0-9]|[a-z]|[_-])*#([0-9]|[a-z])*\.([0-9][a-z]){2,4}$/i","")
at start I used [0-9]|[a-z])([0-9]|[a-z]|[_-] because I didn't want people able to use _- as the start....so forced start as number/letters only
There must be a million different people that wrote a new regex for email validation. If you are interested in the email format you can just use
$email = filter_var($email, FILTER_VALIDATE_EMAIL);
and if the final value is empty the initial one wasn't a valid email address format.
(as an extra step you could try to validate the domain by using this function http://php.net/manual/en/function.checkdnsrr.php)
Have a try with this:
^[0-9a-z_\-]+#[0-9a-z_\-]+\.[0-9a-z]{2,4}$
But as said: there are ready-to-use regexes, much better than trying to reinvent the wheel. Also this current approach does not macth all valid addresses and validates some addresses that are illegal.
Which reason of email validation? It is very upset when you try to enter you email and you can't due to the stupid validation. I think it is enoth to check the availability of '#' and '.' signs, in case user unintentionally missed this.
$res = preg_match("/#[^#\.]*\./", $str);

PHP custom regex

I've written this regex to check for valid emails: /^[-a-z0-9._]+#[-a-z0-9._]+\.+[a-z]{2,6}$/i
I want it to work for emails like name1+name2#domaine.com
How can I fix this regex?
I Have a simpler solution.
if(filter_var($email,FILTER_VALID_EMAIL))
{
//true
}
this would be sufficient in most cases, this actually runs an regular check in C which in turn would be faster but if you wish to have control over the reg-ex in your application then the regex below is what's used for this check:
/^((\\\"[^\\\"\\f\\n\\r\\t\\b]+\\\")|([\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\~\\/\\^\\`\\|\\{\\}\\=\\?]+(\\.[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\~\\/\\^\\`\\|\\{\\}\\=\\?]+)*))#((\\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\\-])+\\.)+[A-Za-z\\-]+))$/D
Another tip i will give you is that a user may enter an email address such as: invalid#dontexists.com which would then bypass your checks for a valid email, if you wan't to make sure that dontexists.com is running an email server is do:
$has_mx_server = (bool)checkdnsrr($domain,"MX");
if the domain has a registered MX Record the chances of the email being faked is reduced by a good chunk.
First part
[-a-z0-9._]+
does not accept right now plus sign. Expand it:
[-+a-z0-9._]+
Try
/^[-a-z0-9._+]+#[-a-z0-9._]+\.+[a-z]{2,6}$/i
Place the + inside the braces and escape it with a backslash
/^[-a-z0-9._\+]+#[-a-z0-9._]+\.+[a-z]{2,6}$/i
"+" is a meta character meaning to search for 1 or more occurrence, therefore, to search for the actual character, it must be escaped.

Categories