There has to be a better regex - php

I'm writing a small CMS and I'm trying to turn a title into a URL slug with dashes. I know I need to do a couple of things and I've got the whole thing work, but I just don't like it. The problem seems to be that if there are any special characters at the end, I'd need to remove them before it goes into the database. The only way I could figure out doing this was to do 2 preg_replace's in one statement. So it looks something like this:
preg_replace("/\-$/","",preg_replace('/[^a-z0-9]+/i', "-", strtolower($title)));
and it and turn this: (this is a title!!!)))**that is (strange))
into this: this-is-a-title-that-is-strange
But this expression just looks like ass. There has to be a better way of coding this, or something out there, I just don't know it. Any help would be greatly appreciated

You can make just one call to preg-replace with array inputs as:
preg_replace( array('/[^a-z0-9]+/','/^-|-$/'), // from array
array('-',''), // to array
strtolower($title));
Note that your existing code retains leading - if any. The code above gets rid of that.

One option, which still requires two replacements but takes care of both the start and end dashes in one pass, is:
preg_replace('/[^a-z0-9]/', '',
preg_replace('/([a-z0-9])[^a-z0-9]+([a-z0-9])/', '$1-$2',
strtolower($title)));
There is also the alternative of:
implode('-',
preg_split('/[^a-z0-9]/',
strtolower($title),
PREG_SPLIT_NO_EMPTY));

Use trim.
trim(preg_replace('/[^a-z0-9]+/i', "-", strtolower($title)), '-')

Related

Using preg_match_all to filter out strings containing this but not this

im having an issue with preg_match_all. I have this string:
$product_req = "ACTIVE-6,CATEGORY-ACTIVE-8,CATEGORY-ACTIVE-4,ACTIVE-9";
I need to get the numbers preceded by "ACTIVE-" but not by "CATEGORY-ACTIVE-", so in this case the result should be 6,9. I used the statement below:
preg_match_all("/ACTIVE-(\d+)/", $product_req, $this_act);
However this will return all the numbers because all of them are in fact preceded by "ACTIVE-" but thats not what i meant because i need to leave out those preceded by "CATEGORY-ACTIVE-". How can i configure preg_match_all to do it? Or maybe there is some other function that can do the job?
EDIT:
I tried this:
preg_match_all("/CATEGORY-ACTIVE-(\d+)/", $product_req, $this_cat_act);
preg_match_all("/ACTIVE-(\d+)/", $product_req, $this_act);
$act_cat = str_replace($this_cat_act[1],"",$this_act[1]);
it kinda works, but i guess there is a better and cleaner way to do it. Besides the output is kinda weird too.
Thank you.

Slugs for SEO using PHP - Appending name to end of URL

Something I have noticed on the StackOverflow website:
If you visit the URL of a question on StackOverflow.com:
"https://stackoverflow.com/questions/10721603"
The website adds the name of the question to the end of the URL, so it turns into:
"https://stackoverflow.com/questions/10721603/grid-background-image-using-imagebrush"
This is great, I understand that this makes the URL more meaningful and is probably good as a technique for SEO.
What I wanted to Achieve after seeing this Implementation on StackOverflow
I wish to implement the same thing with my website. I am happy using a header() 301 redirect in order to achieve this, but I am attempting to come up with a tight script that will do the trick.
My Code so Far
Please see it working by clicking here
// Set the title of the page article (This could be from the database). Trimming any spaces either side
$original_name = trim(' How to get file creation & modification date/times in Python with-dash?');
// Replace any characters that are not A-Za-z0-9 or a dash with a space
$replace_strange_characters = preg_replace('/[^\da-z-]/i', " ", $original_name);
// Replace any spaces (or multiple spaces) with a single dash to make it URL friendly
$replace_spaces = preg_replace("/([ ]{1,})/", "-", $replace_strange_characters);
// Remove any trailing slashes
$removed_dashes = preg_replace("/^([\-]{0,})|([\-]{2,})|([\-]{0,})$/", "", $replace_spaces);
// Show the finished name on the screen
print_r($removed_dashes);
The Problem
I have created this code and it works fine by the looks of things, it makes the string URL friendly and readable to the human eye. However, it I would like to see if it is possible to simplify or "tightened it up" a bit... as I feel my code is probably over complicated.
It is not so much that I want it put onto one line, because I could do that by nesting the functions into one another, but I feel that there might be an overall simpler way of achieving it - I am looking for ideas.
In summary, the code achieves the following:
Removes any "strange" characters and replaces them with a space
Replaces any spaces with a dash to make it URL friendly
Returns a string without any spaces, with words separated with dashes and has no trailing spaces or dashes
String is readable (Doesn't contain percentage signs and + symbols like simply using urlencode()
Thanks for your help!
Potential Solutions
I found out whilst writing this that article, that I am looking for what is known as a URL 'slug' and they are indeed useful for SEO.
I found this library on Google code which appears to work well in the first instance.
There is also a notable question on this on SO which can be found here, which has other examples.
I tried to play with preg like you did. However it gets more and more complicated when you start looking at foreign languages.
What I ended up doing was simply trimming the title, and using urlencode
$url_slug = urlencode($title);
Also I had to add those:
$title = str_replace('/','',$title); //Apache doesn't like this character even encoded
$title = str_replace('\\','',$title); //Apache doesn't like this character even encoded
There are also 3rd party libraries such as: http://cubiq.org/the-perfect-php-clean-url-generator
Indeed, you can do that:
$original_name = ' How to get file creation & modification date/times in Python with-dash?';
$result = preg_replace('~[^a-z0-9]++~i', '-', $original_name);
$result = trim($result, '-');
To deal with other alphabets you can use this pattern instead:
~\P{Xan}++~u
or
~[^\pL\pN]++~u

Regex replace matched subexpression (and nothing else)?

I've used regex for ages but somehow I managed to never run into something like this.
I'm looking to do some bulk search/replace operations within a file where I need to replace some data within tag-like elements. For example, converting <DelayEvent>13A</DelayEvent> to just <DelayEvent>X</DelayEvent> where X might be different for each.
The current way I'm doing this is such:
$new_data = preg_replace('|<DelayEvent>(\w+)</DelayEvent>|', '<DelayEvent>X</DelayEvent>', $data);
I can shorten this a bit to:
$new_data = preg_replace('|(<DelayEvent>)(\w+)(</DelayEvent>)|', '${1}X${2}', $data);
But really all I want to do is simulate a "replace text between tags T with X".
Is there a way to do such a thing? In essence I'm trying to prevent having to match all the surrounding data and reassembling it later. I just want to replace a given matched sub-expression with something else.
Edit: The data is not XML, although it does what appear to be tag-like elements. I know better than parsing HTML and XML with RegEx. ;)
It is possible using lookarounds:
$new_data = preg_replace('|(?<=<DelayEvent>)\w+(?=</DelayEvent>)|', 'X', $data);
See it working online: ideone

PHP - quick regular expression question

so I am trying to match word in a wall of text and return few words before and after the match. Everything is working, but I would like to ask if there is any way to modify it so it will look for similar words. Hmm, let me show you an example:
preg_match_all('/(?:\b(\w+\s+)\{1,5})?.*(pripravená)(?:(\s+){1,2}\b.{1,10})?/u', $item, $res[$file]);
This code returns a match, but I would like it to modify it so
preg_match_all('/(?:\b(\w+\s+)\{1,5})?.*(pripravena)(?:(\s+){1,2}\b.{1,10})?/u', $item, $res[$file]);
would also return a match. Its slovak language and I tried with range of unicode characters and also with \p{Sk} (and few others) but to no avail. Maybe I just put it in the wrong place, I dont know...
Is something like this possible?
Any help is appreciated
I don't know if there is a "ignore accent" switch. But you could replace your search query with something like:
$query = 'pripravená';
$query = preg_replace(
array('=[áàâa]=i','=[óòôo]=i','=[úùûu]=i'),
array( '[áàâa]' , '[óòôo]' , '[úùûu]' ),
$query
);
preg_match_all('/(?:\b(\w+\s+)\{1,5})?.*('.$query.')(?:(\s+){1,2}\b.{1,10})?/u', $item, $res[$file]);
That would convert your 'pripravená' query into 'pripraven[áàâa]'.
You could use strtr() to strip out the accents: See the PHP manual page for a good example - http://php.net/manual/en/function.strtr.php
$addr = strtr($addr, "äåö", "aao");
You'd still need to specify all the relevant characters, but it would be easier than using a regex to do it.
(pripraven[áa]) or (pripravena\p{M}*) or, more likely, some combination of these approaches.
I don't know of any other, more concise, way of specifying "all Latin-1 vowels that are similar to 'a' in my current locale".

Whitelist in php

I have an input for users where they are supposed to enter their phone number. The problem is that some people write their phone number with hyphens and spaces in them. I want to put the input trough a filter to remove such things and store only digits in my database.
I figured that I could do some str_replace() for the whitespaces and special chars.
However I think that a better approach would be to pick out just the digits instead of removing everything else. I think that I have heard the term "whitelisting" about this.
Could you please point me in the direction of solving this in PHP?
Example: I want the input "0333 452-123-4" to result in "03334521234"
Thanks!
This is a non-trivial problem because there are lots of colloquialisms and regional differences. Please refer to What is the best way for converting phone numbers into international format (E.164) using Java? It's Java but the same rules apply.
I would say that unless you need something more fully-featured, keep it simple. Create a list of valid regular expressions and check the input against each until you find a match.
If you want it really simple, simply remove non-digits:
$phone = preg_replace('![^\d]+!', '', $phone);
By the way, just picking out the digits is, by definition, the same as removing everything else. If you mean something different you may want to rephrase that.
$number = filter_var(str_replace(array("+","-"), '', $number), FILTER_SANITIZE_NUMBER_INT);
Filter_Var removes everything but pluses and minuses, and str_replace gets rid of those.
or you could use preg_replace
$number = preg_replace('/[^0-9]/', '', $number);
You could do it two ways. Iterate through each index in the string, and run is_numeric() on it, or you could use a regular expression on the string.
On the client side I do recommand using some formating that you design when creating a form. This is good for zip or telephone fields. Take a look at this jquery plugin for a reference. It will much easy later on the server side.

Categories